diff --git a/gomori/src/board.rs b/gomori/src/board.rs index 1ef6955..e661b1e 100644 --- a/gomori/src/board.rs +++ b/gomori/src/board.rs @@ -44,7 +44,7 @@ struct Diff { /// The effects that playing a card would have. /// -/// Returned by [`Board::execute()`]. +/// Returned by [`Board::calculate()`]. pub struct CalculatedEffects<'a> { /// This struct ties together the board and its diff, to prevent any possible mixups board: &'a Board, @@ -146,12 +146,7 @@ impl Board { let mut set = CardsSet::new(); for &(i, j, field) in &self.fields { if won.contains(i, j) { - for card in field.hidden_cards() { - set = set.insert(card); - } - if let Some(card) = field.top_card() { - set = set.insert(card); - } + set |= field.all_cards(); } } set @@ -260,6 +255,17 @@ impl Board { bitboard } + /// Returns all the coordinates that already have a card on them and are valid places to play the given card. + pub fn combo_locations_for_card(&self, card: Card) -> BitBoard { + let mut bitboard = BitBoard::empty_board_centered_at(self.fields[0].0, self.fields[0].1); + for &(i, j, field) in &self.fields { + if field.can_place_card(card) { + bitboard = bitboard.insert(i, j); + } + } + bitboard + } + /// Returns a [`CompactField`] if there are any cards at the given coordinate. pub fn get(&self, i: i8, j: i8) -> Option { for &(i_field, j_field, compact_field) in &self.fields { @@ -499,6 +505,11 @@ mod python { self.locations_for_card(card) } + #[pyo3(name = "combo_locations_for_card")] + fn py_combo_locations_for_card(&self, card: Card) -> BitBoard { + self.combo_locations_for_card(card) + } + #[pyo3(name = "get")] fn py_get(&self, i: i8, j: i8) -> Option { self.get(i, j) diff --git a/gomori/src/board/compact_field.rs b/gomori/src/board/compact_field.rs index e46452e..991831e 100644 --- a/gomori/src/board/compact_field.rs +++ b/gomori/src/board/compact_field.rs @@ -87,7 +87,7 @@ impl CompactField { /// All cards on the field. /// - /// Equal to [`hidden_cards()`] + [`top_card()`], if any. + /// Equal to [`hidden_cards()`](Self::hidden_cards) + [`top_card()`](Self::top_card), if any. pub fn all_cards(self) -> CardsSet { if let Some(c) = self.top_card() { self.hidden_cards().insert(c) diff --git a/gomori/src/cards_set.rs b/gomori/src/cards_set.rs index f881738..3cb09ff 100644 --- a/gomori/src/cards_set.rs +++ b/gomori/src/cards_set.rs @@ -5,6 +5,16 @@ use crate::Card; /// Allows intersection/union/xor with other such sets via bitwise ops. /// Also implements [`IntoIterator`], so it can be converted into e.g. /// a vector with `Vec::from_iter(cards_set)`. +/// +/// ``` +/// use gomori::{card, CardsSet}; +/// let mut set = CardsSet::new(); +/// // This is an immutable data type, so functions like `insert` return a new `CardsSet`. +/// set = set.insert(card!("7♥")); +/// set = set.insert(card!("7♥")); // Inserting a second time has no effect +/// set = set.insert(card!("2♥")); +/// assert_eq!(Vec::from_iter(set), vec![card!("2♥"), card!("7♥")]); +/// ``` #[cfg_attr(feature = "python", pyo3::pyclass)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct CardsSet { diff --git a/gomori/src/errors.rs b/gomori/src/errors.rs index 4fbc796..0cae75c 100644 --- a/gomori/src/errors.rs +++ b/gomori/src/errors.rs @@ -124,7 +124,7 @@ mod python { gomori, IllegalMoveException, pyo3::exceptions::PyException, - "Describes why the card cannot be played." + "Describes why a move is illegal." ); impl From for PyErr { diff --git a/gomori/src/protocol_types.rs b/gomori/src/protocol_types.rs index 100d99d..a5b770a 100644 --- a/gomori/src/protocol_types.rs +++ b/gomori/src/protocol_types.rs @@ -45,6 +45,7 @@ pub enum Request { #[derive(Clone, Debug, Serialize, Deserialize)] pub struct Okay(); +/// Black or white. #[cfg_attr(feature = "python", pyo3::pyclass)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")]