/******************************************************************************* * Solitaire * * Copyright (C) 2016 by Martin P. Robillard * * See: https://github.com/prmr/Solitaire * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package ca.mcgill.cs.stg.solitaire.model; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; import ca.mcgill.cs.stg.solitaire.cards.Card; import ca.mcgill.cs.stg.solitaire.cards.Card.Rank; import ca.mcgill.cs.stg.solitaire.cards.Deck; import ca.mcgill.cs.stg.solitaire.model.GameModel.StackIndex; /** * Manages the state of the bottom stacks where partial * suits are accumulated. */ class WorkingStackManager { private static final int NUMBER_OF_CARDS_NEEDED = 24; private Map<StackIndex, Stack<Card>> aStacks = new HashMap<>(); private Set<Card> aVisible = new HashSet<>(); /** * Creates working stacks with no cards in them. */ WorkingStackManager() { for( StackIndex index : StackIndex.values() ) { aStacks.put(index, new Stack<Card>()); } } /** * Fills the working stacks by drawing cards from the deck. * @param pDeck a deck of card to use to fill the stacks initially. * @pre pDeck != null && pDeck.size() >= 24 */ void initialize(Deck pDeck) { assert pDeck != null && pDeck.size() >= NUMBER_OF_CARDS_NEEDED; aVisible.clear(); for( int i = 0; i < StackIndex.values().length; i++ ) { aStacks.get(StackIndex.values()[i]).clear(); for( int j = 0; j < i+1; j++ ) { Card card = pDeck.draw(); aStacks.get(StackIndex.values()[i]).push(card); if( j == i ) { aVisible.add(card); } } } } /** * Determines if it is legal to move pCard on top of * stack pIndex, i.e. if a king is moved to an empty * stack or any other rank on a card of immediately greater * rank but of a different color. * @param pCard The card to move * @param pIndex The destination stack. * @return True if the move is legal. * @pre pCard != null, pIndex != null */ boolean canMoveTo(Card pCard, StackIndex pIndex ) { assert pCard != null && pIndex != null; Stack<Card> stack = aStacks.get(pIndex); if( stack.isEmpty() ) { return pCard.getRank() == Rank.KING; } else { return pCard.getRank().ordinal() == stack.peek().getRank().ordinal()-1 && !pCard.sameColorAs(stack.peek()); } } /** * @param pIndex The index of the stack to obtain. * @return An array of cards in the stack at pIndex, where element [0] is the * bottom of the stack. Modifying the array has no impact on the state of the * object. * @pre pIndex != null */ Card[] getStack(StackIndex pIndex) { assert pIndex != null; return aStacks.get(pIndex).toArray(new Card[aStacks.get(pIndex).size()]); } /** * Returns true if moving pCard away reveals the top of the card. * @param pCard The card to test * @param pIndex The index of the stack. * @return true if the card above pCard is not visible and pCard * is visible. * @pre pCard != null && pIndex != null */ boolean revealsTop(Card pCard, StackIndex pIndex) { assert pCard != null && pIndex != null; int indexOf = aStacks.get(pIndex).indexOf(pCard); if( indexOf < 1 ) { return false; } return aVisible.contains(pCard) && !aVisible.contains(aStacks.get(pIndex).get(indexOf-1)); } /** * Move pCard and all the cards below to pDestination. * @param pCard The card to move, possibly including all the cards on top of it. * @param pOrigin The location of the card before the move. * @param pDestination The intended destination of the card. * @pre this is a legal move */ void moveWithin(Card pCard, StackIndex pOrigin, StackIndex pDestination ) { assert pCard != null && pOrigin != null && pDestination != null; assert contains(pCard, pOrigin); assert isVisible(pCard); Stack<Card> temp = new Stack<>(); Card card = aStacks.get(pOrigin).pop(); temp.push(card); while( card != pCard ) { card = aStacks.get(pOrigin).pop(); temp.push(card); } while( !temp.isEmpty() ) { aStacks.get(pDestination).push(temp.pop()); } } /** * Returns a sequence of cards starting at pCard and including * all cards on top of it. * @param pCard The bottom card in the stack * @param pIndex The index of the stack. * @return An array of cards in the stack. Element 0 is the bottom. * @pre pCard != null && pIndex != null */ public Card[] getSequence(Card pCard, StackIndex pIndex) { Stack<Card> stack = aStacks.get(pIndex); List<Card> lReturn = new ArrayList<>(); boolean aSeen = false; for( Card card : stack ) { if( card == pCard ) { aSeen = true; } if( aSeen ) { lReturn.add(card); } } return lReturn.toArray(new Card[lReturn.size()]); } /** * Make the top card of a stack visible. * @param pIndex The index of the requested stack. * @pre pIndex != null && !isEmpty(pIndex) */ void showTop(StackIndex pIndex) { assert !aStacks.get(pIndex).isEmpty(); aVisible.add(aStacks.get(pIndex).peek()); } /** * Make the top card of a stack not visible. * @param pIndex The index of the requested stack. * @pre pIndex != null && !isEmpty(pIndex) */ void hideTop(StackIndex pIndex) { assert !aStacks.get(pIndex).isEmpty(); aVisible.remove(aStacks.get(pIndex).peek()); } /** * @param pCard The card to check * @param pIndex The index of the stack to check * @return True if pIndex contains pCard * @pre pCard != null && pIndex != null */ boolean contains(Card pCard, StackIndex pIndex) { assert pCard != null && pIndex != null; for( Card card : aStacks.get(pIndex)) { if( card == pCard ) { return true; } } return false; } /** * @param pCard The card to check. * @return Whether pCard is contains in any stack. * @pre pCard != null; */ boolean contains(Card pCard) { assert pCard != null; for( StackIndex index : StackIndex.values()) { if( contains(pCard, index)) { return true; } } return false; } /** * @param pCard The card to check. * @return true if pCard is visible in the stacks. * @pre contains(pCard) */ boolean isVisible(Card pCard) { assert contains(pCard); return aVisible.contains(pCard); } /** * Removes the top card from the stack at pIndex. * @param pIndex The index of the stack to pop. * @pre !isEmpty(pIndex) */ void pop(StackIndex pIndex) { assert !aStacks.get(pIndex).isEmpty(); aVisible.remove(aStacks.get(pIndex).pop()); } /** * Places a card on top of the stack at pIndex. The * card will be visible by default. * @param pCard The card to push. * @param pIndex The index of the destination stack. * @pre pCard != null && pIndex != null; */ void push(Card pCard, StackIndex pIndex) { assert pCard != null && pIndex != null; aStacks.get(pIndex).push(pCard); aVisible.add(pCard); } }