LibCards: Support non-alternating colour patience games

This introduces a new MovementType concept to LibCards, starting the
process to allow other patience games to be implemented using it - that
differ more substantially from Klondike in logic.

This is currently used for two purposes: 1. to verify that the
'grabbed' stack of cards is valid* (sequential and correct colours) and
2. to allow 'grabbed' stacks to be pushed onto same-colour,
either-colour, or alternating-colour stacks

* Klondike doesn't need this logic, as per how the game works any
  'grabbed' selection is guaranteed to be valid.
This commit is contained in:
Jamie Mansfield 2021-06-16 01:29:08 +01:00 committed by Andreas Kling
parent 84c0f98fb2
commit b7e806e15e
Notes: sideshowbarker 2024-07-18 11:35:30 +09:00
2 changed files with 67 additions and 7 deletions

View File

@ -108,7 +108,7 @@ void CardStack::rebound_cards()
card.set_position(m_stack_positions.at(card_index++)); card.set_position(m_stack_positions.at(card_index++));
} }
void CardStack::add_all_grabbed_cards(const Gfx::IntPoint& click_location, NonnullRefPtrVector<Card>& grabbed) void CardStack::add_all_grabbed_cards(const Gfx::IntPoint& click_location, NonnullRefPtrVector<Card>& grabbed, MovementRule movement_rule)
{ {
VERIFY(grabbed.is_empty()); VERIFY(grabbed.is_empty());
@ -149,15 +149,56 @@ void CardStack::add_all_grabbed_cards(const Gfx::IntPoint& click_location, Nonnu
grabbed.append(*last_intersect); grabbed.append(*last_intersect);
last_intersect->set_moving(true); last_intersect->set_moving(true);
} }
// verify valid stack
bool valid_stack = true;
uint8_t last_value;
Color last_color;
for (size_t i = 0; i < grabbed.size(); i++) {
auto& card = grabbed.at(i);
if (i != 0) {
bool color_match;
switch (movement_rule) {
case Alternating:
color_match = card.color() != last_color;
break;
case Same:
color_match = card.color() == last_color;
break;
case Any:
color_match = true;
break;
}
if (!color_match || card.value() != last_value - 1) {
valid_stack = false;
break;
}
}
last_value = card.value();
last_color = card.color();
}
if (!valid_stack) {
for (auto& card : grabbed) {
card.set_moving(false);
}
grabbed.clear();
}
} }
bool CardStack::is_allowed_to_push(const Card& card, size_t stack_size) const bool CardStack::is_allowed_to_push(const Card& card, size_t stack_size, MovementRule movement_rule) const
{ {
if (m_type == Stock || m_type == Waste || m_type == Play) if (m_type == Stock || m_type == Waste || m_type == Play)
return false; return false;
if (m_type == Normal && is_empty()) if (m_type == Normal && is_empty()) {
return card.value() == 12; // FIXME: proper solution for this
if (movement_rule == Alternating) {
return card.value() == 12;
}
return true;
}
if (m_type == Foundation && is_empty()) if (m_type == Foundation && is_empty())
return card.value() == 0; return card.value() == 0;
@ -173,7 +214,20 @@ bool CardStack::is_allowed_to_push(const Card& card, size_t stack_size) const
return false; return false;
return top_card.type() == card.type() && m_stack.size() == card.value(); return top_card.type() == card.type() && m_stack.size() == card.value();
} else if (m_type == Normal) { } else if (m_type == Normal) {
return top_card.color() != card.color() && top_card.value() == card.value() + 1; bool color_match;
switch (movement_rule) {
case Alternating:
color_match = card.color() != top_card.color();
break;
case Same:
color_match = card.color() == top_card.color();
break;
case Any:
color_match = true;
break;
}
return color_match && top_card.value() == card.value() + 1;
} }
VERIFY_NOT_REACHED(); VERIFY_NOT_REACHED();

View File

@ -24,6 +24,12 @@ public:
Foundation Foundation
}; };
enum MovementRule {
Alternating,
Same,
Any,
};
CardStack(); CardStack();
CardStack(const Gfx::IntPoint& position, Type type); CardStack(const Gfx::IntPoint& position, Type type);
CardStack(const Gfx::IntPoint& position, Type type, NonnullRefPtr<CardStack> associated_stack); CardStack(const Gfx::IntPoint& position, Type type, NonnullRefPtr<CardStack> associated_stack);
@ -44,8 +50,8 @@ public:
void move_to_stack(CardStack&); void move_to_stack(CardStack&);
void rebound_cards(); void rebound_cards();
bool is_allowed_to_push(const Card&, size_t stack_size = 1) const; bool is_allowed_to_push(const Card&, size_t stack_size = 1, MovementRule movement_rule = Alternating) const;
void add_all_grabbed_cards(const Gfx::IntPoint& click_location, NonnullRefPtrVector<Card>& grabbed); void add_all_grabbed_cards(const Gfx::IntPoint& click_location, NonnullRefPtrVector<Card>& grabbed, MovementRule movement_rule = Alternating);
void draw(GUI::Painter&, const Gfx::Color& background_color); void draw(GUI::Painter&, const Gfx::Color& background_color);
void clear(); void clear();