GameEngine.java :  » Game » ap-kings-in-the-corner » com » asparagusprograms » kingsinthecorner » Android Open Source

Android Open Source » Game » ap kings in the corner 
ap kings in the corner » com » asparagusprograms » kingsinthecorner » GameEngine.java
/**
 * Copyright 2010 Trevor Boyce
 * 
 * This file is part of Kings in the Corner.
 *
 *    Kings in the Corner 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.
 *
 *    Kings in the Corner 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 Kings in the Corner.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.asparagusprograms.kingsinthecorner;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.text.DecimalFormat;
import java.util.Observable;

import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.preference.PreferenceManager;
import android.view.MotionEvent;
import android.widget.Toast;


/** Handles game functions and provides access to necessary information */
public class GameEngine extends Observable implements Runnable {
  // Types for computer errors
  private final static int STYLE_MOVE = 1,
               STYLE_PLAY = 2;
  
  // User preferences
  private boolean mDrawPileCount, mHighlightCards, mAutosave, mEmptyDeckWarning,
          mWarnedEmpty, mSortHand;
  private String mUsername; // Only set during creation so it is not possible to switch mid-game
  private String mCardStyle;
  private boolean mShowComputerHand; // Cheats
  private int mComputerDelay;
  private int mDifficulty; // Higher is easier

  // Global variables
  private Context mContext;
  private SharedPreferences mPrefs;
  private String mSaveString;
  private int mPlayerCount;
  private int mTurn;    
  private int mWinner;
  private volatile boolean mPaused, mStop;

  //Undo variables
  private boolean mCanUndo;
  private boolean mUndoIsSide;
  private int mUndoPos;
  private Card mUndoCard;
  private Card mReplaceWithCard;

  //Hand, deck, etc
  private Hand[] mHands;
  private Deck mDeck;
  private Pile[] mSides, mCorners;
  private int mSelectedCard;    // -1=none
  private int mSelectedPile;    // -1=none
  private int mHighlightedPile;  // -1=none

  // Drawing variables
  private boolean mDrawInitialized;
  private Paint mPaint;
  private int mViewHeight, mViewWidth, mCardHeight, mCardWidth, mScreenHeight;
  private boolean mHideHand;
  private CardTableView mTable;
  private int tarx, tary;

  // Stored bitmaps
  private Bitmap[] mPlayerBitmaps;
  private Bitmap mCardBack;
  private Bitmap mGlowNormal, mGlowSide, mGlowCorner1, mGlowCorner2;

  //Rects for placing bitmaps
  private Rect draw;

  /** Creates a new GameEngine */
  public GameEngine(Context context, int numPlayers, CardTableView table) {
    mContext = context;
    mTable = table;
    mPlayerCount = numPlayers;
    
    Initialize();
  }
  
  /** Initialize global variables **/
  private void Initialize() {
    mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext);
    mUsername = mPrefs.getString(mContext.getResources().getString(R.string.pref_key_username), mContext.getResources().getString(R.string.username_none));
    mCardStyle = mPrefs.getString(mContext.getResources().getString(R.string.pref_key_cardImage), mContext.getResources().getString(R.string.cardImage_default));
    mDifficulty = Integer.parseInt(mPrefs.getString(mContext.getResources().getString(R.string.pref_key_difficulty), "0"));
    updatePrefs();
    mSaveString = mUsername + "_save.dat";
    mTurn = -1;
    mWinner = -1;
    
    if (mPlayerCount == 1) {
      mHideHand = false;
    } else {
      mHideHand = true;
    }
    
    if (mPlayerCount > 1) mPlayerBitmaps = new Bitmap[mPlayerCount];
    switch (mPlayerCount) {
    case 4:
      mPlayerBitmaps[3] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.player4);
    case 3:
      mPlayerBitmaps[2] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.player3);
    case 2:
      mPlayerBitmaps[1] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.player2);
      mPlayerBitmaps[0] = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.player1);
      break;
    default:
      break;
    }
    
    if (mCardStyle != null && mCardStyle.equals("classic")) {
      mCardBack = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.card_backc);
    }
    else {
      mCardBack = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.card_back);
    }
    mGlowNormal = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.glow);
    mGlowSide = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.glow_side);
    mGlowCorner1 = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.glow_corner1);
    mGlowCorner2 = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.glow_corner2);
    
    mPaint = new Paint();
    mSelectedCard = -1;
    mSelectedPile = -1;
    mHighlightedPile = -1;
    mCanUndo = false;
    
    int count = (mPlayerCount == 1) ? 2 : mPlayerCount;  // Number of hands to deal, if it's a single player game this needs to be 2
    mHands = new Hand[count];
    
    mWarnedEmpty = false;
    mDrawInitialized = false;
    mPaused = false;
    mStop = false;
    mDeck = new Deck(mContext, mCardStyle);
    mSides = new Pile[4];
    mCorners = new Pile[4];
    draw = new Rect();
  }

  // Cheats
  /** Auto Win **/
  public void autoWin() {
    mHands[0].clear();
    playerWin();
  }

  /** Trash opponent **/
  public void trash() {
    Card joker = new Card(0, 4);
    joker.setImage(mContext, mCardStyle);
    mHands[1].addCard(joker);
    mTable.postInvalidate();
  }

  /** Returns the current turn */
  public int turn() {return mTurn;}

  /** Returns the winner if there is one else returns -1*/
  public int winner() {return mWinner;}

  /** Returns true if the empty deck warning should be displayed */
  public boolean warnEmpty() {
    if (mDeck.cardsLeft() == 0 && mEmptyDeckWarning && !mWarnedEmpty) {
      mWarnedEmpty = true;
      return true;
    }
    return false;
  }

  /** Returns if the game is a single player game */
  public boolean isSinglePlayer() {return (mPlayerCount == 1);}

  /** Whether or not there is a move that can be undone */
  public boolean canUndo() {
    if (mUndoCard == null) return false;
    return mCanUndo;
    }

  /** Set whether to hide the current hand */
  public void hideHand(boolean bool) {
    mHideHand = bool;
    mTable.postInvalidate();
  }

  /** Set that the game is no longer in firstGame mode */
  public void firstGame() {
    mPrefs.edit().putBoolean(mContext.getResources().getString(R.string.pref_key_firstGame), false).commit();
  }
  
  /** Sets whether or not the user is currently in a game **/
  public void inGame(boolean bool) {
    mPrefs.edit().putBoolean(mContext.getResources().getString(R.string.pref_key_inGame), bool).commit();
  }

  //Gameplay methods
  /** Undo the last move */
  public void undo() {
    if (mUndoIsSide) {
      mSides[mUndoPos].undo(mReplaceWithCard);
    } else {
      mUndoCard.setImage(mContext, mCardStyle);
      mCorners[mUndoPos].undo(mReplaceWithCard);
    }
    mUndoCard.setRotate(Card.NORMAL);
    mHands[mTurn].addCard(mUndoCard);
    mCanUndo = false;
    
    mHands[mTurn].sortByColor();
    
    mTable.postInvalidate();
  }

  /** Pause the computer playing */
  public void pause() {
    mPaused = true;
    if (mAutosave) save();
  }

  /** Resume the computer playing */
  public void resume() {mPaused = false;}
  
  public void start() {
    Thread thread = new Thread(this);
      thread.start();
  }
  
  public void stop() {mStop = true;}
  
  /** Whether the computer makes a mistake or not. Based on difficulty and type of play */
  private boolean computerError(int type) {
    if (mDifficulty == 0) return false; // No errors on hard
    int percent = 0;

    switch (type) {
    case STYLE_MOVE:
      percent = (mDifficulty == 1) ? 20 : 60;
      break;
    case STYLE_PLAY:
      percent = mDifficulty * 10;
      break;
    default:
      return false;
    }

    int chance = (int)(java.lang.Math.random()*100);
    return (chance <= percent);
  }

  /** The computer playing */
  @Override
  public void run() {
    try {
    // Dirty way of handling exiting since the thread tries to run after the
    // variables have been freed up, causing a NullPointerException.

    boolean playing = true;
    while (playing) {
      mTable.postInvalidate();
      playing = false;
      // Check if the computer has won or only has a Joker
      if (compWin() || (mHands[1].getCardCount() == 1 && mHands[1].getCard(0).getSuit() == 4) || mStop) return;

      // Wait before playing
      try {
        Thread.sleep(mComputerDelay);
      } catch (InterruptedException e) {}

      // Try to move side piles
      for (int i = 0; i < mSides.length; i++) {
        // Try moving to corners
        for (int j = 0; j < mCorners.length; j++) {
          while(mPaused) { // Wait while the game is paused
            if (mStop) return;
            try {
              Thread.sleep(1000);
            } catch (InterruptedException e) {}
          }  
          if (!computerError(STYLE_MOVE) && mSides[i].moveTo(mCorners[j])) {
            playing = true;
            mTable.postInvalidate();
            // Wait
            try {
              Thread.sleep(mComputerDelay);
            } catch (InterruptedException e) {}
          }
        }
        // Try moving to other sides
        for (int j = 0; j < mSides.length; j++) {
          while(mPaused) { // Wait while the game is paused
            if (mStop) return;
            try {
              Thread.sleep(1000);
            } catch (InterruptedException e) {}
          }
          if (!computerError(STYLE_MOVE) && j != i && mSides[i].moveTo(mSides[j])) {
            playing = true;
            mTable.postInvalidate();
            // Wait
            try {
              Thread.sleep(mComputerDelay);
            } catch (InterruptedException e) {}
          }
        }
      }

      // Try to play cards in hand
      for (int i = 0; i < mHands[1].getCardCount(); i++) {
        // Try to play on corners
        for (int j = 0; j < mCorners.length; j++) {
          while(mPaused) { // Wait while the game is paused
            if (mStop) return;
            try {
              Thread.sleep(1000);
            } catch (InterruptedException e) {}
          }
          if (!computerError(STYLE_PLAY) && mCorners[j].play(mHands[1].getCard(i))) {
            mHands[1].removeCard(i);
            playing = true;
            mTable.postInvalidate();
            // Wait
            try {
              Thread.sleep(mComputerDelay);
            } catch (InterruptedException e) {}
            break;
          }
        }
        // Break if a card was played since the card count and position has changed
        if (playing) break;
        // Try to play on sides
        for (int j = 0; j < mSides.length; j++) {
          while(mPaused) { // Wait while the game is paused
            if (mStop) return;
            try {
              Thread.sleep(1000);
            } catch (InterruptedException e) {}
          }
          if ( (mSides[j].first == null || !computerError(STYLE_PLAY)) && mSides[j].play(mHands[1].getCard(i)) ) {
            mHands[1].removeCard(i);
            playing = true;
            mTable.postInvalidate();
            // Wait
            try {
              Thread.sleep(mComputerDelay);
            } catch (InterruptedException e) {}
            break;
          }
        }
        // Break if a card was played since the card count and position has changed
        if (playing) break;
      }
    }    
    while (mPaused) { // Wait while the game is paused
      if (mStop) return;
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {}
    }
    for (int i = 0; i < 4; i++) mCorners[i].clearCorner();
    if (!playing) nextTurn();

    } catch (NullPointerException e) {}
  }

  /** Handles going to the next player's turn */
  public void nextTurn() {
    mCanUndo = false;
    mSelectedPile = -1;
    mSelectedCard = -1;
    if (mPlayerCount > 1) {
      mHideHand = true;
    } else {
      if (mAutosave) save();
    }
    
    mTurn++;
    if (mPlayerCount > 1 && mTurn >= mPlayerCount) mTurn = 0;
    else if (mPlayerCount == 1 && mTurn >= 2) mTurn = 0;
    if (mDeck.cardsLeft() > 0) {
      mHands[mTurn].addCard(mDeck.dealCard());
    }
    mHands[mTurn].sortByColor();
    
    setChanged();
    notifyObservers();
    mTable.postInvalidate();
    
    if (mPlayerCount == 1 && mTurn == 1) start();
  }

  /** Sets up a new game */
  public void newGame() {
    // Set the deck and shuffle it
    mDeck.shuffle();
    
    // Deal the starting hands
    for (int i = 0; i < mHands.length; i++) {
      mHands[i] = new Hand();
      for (int j = 0; j < 7; j++) {
        mHands[i].addCard(mDeck.dealCard());
      }
    }
    
    // Deal the side piles
    for (int i = 0; i < 4; i++) {
      mSides[i] = new Pile(i, mDeck.dealCard(), null);
    }
    
    // Create empty corner piles
    for (int i = 0; i < 4; i++) {
      mCorners[i] = new Pile(i+4, null, null);
    }
    
    // Set the default Sort Hand option
    if (mPlayerCount == 1 && mSortHand) {
      mHands[0].toggleSortColor();
    }
    
    nextTurn();
  }

  /** Saves the current game */
  public void save() {
    if (mWinner == -1 && mPlayerCount == 1 && mDeck != null) {
      try {
        FileOutputStream fop = mContext.openFileOutput(mSaveString, Context.MODE_PRIVATE);
        ObjectOutputStream out = new ObjectOutputStream(fop);

        Pile[] sides = mSides;
        Pile[] corners = mCorners;
        out.writeInt(mTurn);
        out.writeInt(mHands[0].getCardCount());
        for (int i = 0; i < mHands[0].getCardCount(); i++) {
          out.writeObject(mHands[0].getCard(i));
        }
        out.writeInt(mHands[1].getCardCount());
        for (int i = 0; i < mHands[1].getCardCount(); i++) {
          out.writeObject(mHands[1].getCard(i));
        }
        out.writeInt(mDeck.cardsLeft());
        for (int i = 0; i < 52; i++) {
          out.writeObject(mDeck.cardAt(i));
        }

        for (int i = 0; i < 4; i++) {
          out.writeObject(sides[i]);
        }
        for (int i = 0; i < 4; i++) {
          out.writeObject(corners[i]);
        }
        out.writeBoolean(mCanUndo);
        out.writeObject(mUndoCard);
        out.writeObject(mReplaceWithCard);
        out.writeInt(mUndoPos);
        out.writeBoolean(mUndoIsSide);
        
        out.close();
        fop.close();
      } catch (FileNotFoundException e) {
        mTable.post(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_fileNotFoundException), Toast.LENGTH_SHORT).show();
          }
        });
      } catch (StreamCorruptedException e) {
        mTable.post(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_streamCorruptedException), Toast.LENGTH_SHORT).show();
          }
        });
      } catch (IOException e) {
        mTable.post(new Runnable() {
          @Override
          public void run() {
            Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_IOException), Toast.LENGTH_SHORT).show();
          }
        });
      }
    }
  }

  /** Attempts to restore a saved game */  
  public boolean restore() {
    if (mPlayerCount == 1) {
      try {
        mContext.openFileInput(mSaveString);
        if (restoreGame()) {
          if (mTurn == 1) start();
          mTable.postInvalidate();
        //  if (mTurn == 1) start(); // Start the computer playing since it is their turn
          return true; // Return true that a game was restored
        }
      } catch (FileNotFoundException e) {
        // There is no save game file to restore
      }
    }
    return false; // Game was not restored
  }

  /** Deletes the saved game for the current user */
  public void deleteSave() {
    try {
      mContext.deleteFile(mSaveString);
    } catch (NullPointerException e) {
      mTable.post(new Runnable() {
        @Override
        public void run() {
          Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_nullPointerException), Toast.LENGTH_SHORT).show();
        }
      });
    }
  }

  /** Toggles the "sortedness" of the current hand */
  public void sortHand() {
    mHands[mTurn].toggleSortColor();
    mHands[mTurn].sortByColor();
    mTable.postInvalidate();
  }

  /** Whether the current hand is sorted or not **/
  public boolean handSorted() {
    if (mHands == null || mHands[mTurn] == null) {
      return false;
    }
    return mHands[mTurn].isSortedColor();
  }

  // Drawing methods
  public boolean drawInitialized() {
    return mDrawInitialized;
  }
  
  /** Set the height of the screen */
  public void screenHeight(int height) {mScreenHeight = height;}

  /** Called whenever the screen is being drawn */
  public void onDraw(Canvas c) {
    if (mStop) return;
    if (!mDrawInitialized) {
      // Set scaled dimensions
      mCardWidth = mCardBack.getScaledWidth(c);
      mCardHeight = mCardBack.getScaledHeight(c);
      mViewWidth = mTable.getViewWidth();
      mViewHeight = mTable.getViewHeight();
      
      // Set up positions for side piles
      int x = (mViewWidth/2)-(mCardWidth/2)-mCardHeight-10-(mCardHeight/5);
      int y = (mViewHeight/2)-(mCardWidth/2)-(mCardHeight/4);
      mSides[0].pos = new Rect(x, y, x+mCardHeight+(mCardHeight/5), y+mCardWidth);
      x = (mViewWidth/2)-(mCardWidth/2);
      y = (mViewHeight/2)-(mCardHeight/2)-(mCardHeight/4)-mCardHeight-10-(mCardHeight/5);
      mSides[1].pos = new Rect(x, y, x+mCardWidth, y+mCardHeight+(mCardHeight/5));
      x = (mViewWidth/2)+(mCardWidth/2)+10;
      y = (mViewHeight/2)-(mCardWidth/2)-(mCardHeight/4);
      mSides[2].pos = new Rect(x, y, x+mCardHeight+(mCardHeight/5), y+mCardWidth);
      x = (mViewWidth/2)-(mCardWidth/2);
      y = (mViewHeight/2)+(mCardHeight/2)-(mCardHeight/4)+10;
      mSides[3].pos = new Rect(x, y, x+mCardWidth, y+mCardHeight+(mCardHeight/5));
      
      // Set up positions for corner piles
      x = (mViewWidth/2)-(mCardHeight/2)-mCardHeight-(mCardHeight/8);
      y = (mViewHeight/2)-(mCardHeight/2)-(mCardHeight/4)-mCardHeight-(mCardHeight/8);
      mCorners[0].pos = new Rect(x, y, 0, 0);
      x = (mViewWidth/2)+(mCardWidth/2)+(mCardHeight/6);
      y = (mViewHeight/2)-(mCardHeight/3)-(mCardHeight/2)-mCardHeight-(mCardHeight/8);
      mCorners[1].pos = new Rect(x, y, 0, 0);
      x = (mViewWidth/2)+(mCardWidth/2)+(mCardHeight/6);
      y = (mViewHeight/2)+(mCardHeight/2)-(mCardHeight/4);
      mCorners[2].pos = new Rect(x, y, 0, 0);
      x = (mViewWidth/2)-(mCardHeight/2)-mCardHeight-(mCardHeight/8);
      y = (mViewHeight/2)+(mCardHeight/2)-(mCardHeight/4);
      mCorners[3].pos = new Rect(x, y, 0, 0);
      
      // Set up rectangle for the draw pile
      x = (mViewWidth/2)-(mCardWidth/2);
      y = (mViewHeight/2)-(mCardHeight/2)-(mCardHeight/4);
      draw.set(x, y, x+mCardWidth, y+mCardHeight);
      mDrawInitialized = true;
    }
    if (mDeck != null) {
      if (mWinner == -1) {  // Nobody has won so draw the game data
        if (mDeck.cardsLeft() != 0) {  // Deck isn't empty so draw the deck
          c.drawBitmap(mCardBack, draw.left, draw.top, null);
          if (mDrawPileCount) {  // Print the number of cards in the draw pile
            if (mCardStyle.equals("simple")) mPaint.setColor(Color.WHITE);
            else mPaint.setColor(Color.BLACK);

            mPaint.setTextSize(mCardHeight/2);
            mPaint.setAntiAlias(true);
            DecimalFormat df = new DecimalFormat("00");
            String count = df.format(mDeck.cardsLeft());
            int height = (int) mPaint.descent();
            int width = (int) mPaint.measureText(count);
            c.drawText(count, (mViewWidth/2)-(width/2), (mViewHeight/2)-(height), mPaint);
          }
        }
        if (mHands[mTurn] != null && !mHideHand) {
          drawHand(c);
          if (mPlayerCount > 1) {
            c.drawBitmap(mPlayerBitmaps[mTurn], 0, 0, null); // Draws the current turn
          }
          // Draw the scores for each player
          if (mPlayerCount > 1) {
            mPaint.setTextSize(mCardHeight/6);
            mPaint.setColor(Color.BLACK);
            mPaint.setAntiAlias(true);
            for (int i = 0; i < mPlayerCount; i++) {
              String s = "Player " + (i+1) + ": " + mHands[i].getCardCount() + " ";
              c.drawText(s, (int)((mViewWidth)-mPaint.measureText(s)-2), (int)((i+1)*(mPaint.descent()-mPaint.ascent())), mPaint);
            }
          }
          else drawComputerHand(c);
        }
        if (mSides != null) {
          // Loop through and draw each side
          for (int i = 0; i < mSides.length; i++) {
            if (i != mSelectedPile && mSides[i] != null) {
              mSides[i].draw(c, mCardHeight);
            }
          }
        }
        if (mCorners != null) {
          // Loop through and draw each corner
          for (int i = 0; i < mCorners.length; i++) {
            if (mCorners[i] != null) mCorners[i].draw(c, mCardHeight);
          }
        }
        if (mSides != null && mCorners != null && mHighlightCards) {
          drawHighlighted(c);
        }
        // Draw the selected card/pile
        if (mSelectedCard >= 0) {
          Card card = mHands[mTurn].getCard(mSelectedCard);
          card.setRotate(Card.NORMAL);
          c.drawBitmap(card.getImage(), tarx-(mCardWidth/2), tary-(mCardHeight/6), null);
        } else if (mSelectedPile >= 0)
          mSides[mSelectedPile].drawSelected(c, mCardWidth, mCardHeight, tarx, tary);
      } else {
        mPaint.setColor(Color.BLACK);
        mPaint.setTextSize(mCardHeight/4);
        mPaint.setAntiAlias(true);
        String s1 = "Press Menu to play again";
        String s2 = "or Back to exit.";
        c.drawText(s1, (mViewWidth/2)-(int)(mPaint.measureText(s1)/2), (mViewHeight/2)-(int)mPaint.descent(), mPaint);
        c.drawText(s2, (mViewWidth/2)-(int)(mPaint.measureText(s2)/2), (mViewHeight/2)+(int)mPaint.descent()-(int)mPaint.ascent(), mPaint);
        mTable.postInvalidate();
      }        
    }
  }

  /** Handles user touch events on the given View */
  public boolean onTouchEvent(MotionEvent e) {
    if ( (!(mPlayerCount == 1 && mTurn == 1) || mPlayerCount > 1) && mWinner == -1) {  // Player's turn so handle touch events
      int eventaction = e.getAction();
      tarx=(int)e.getRawX();
      tary=(int)e.getRawY()-(mScreenHeight-mViewHeight);
      switch (eventaction) { 
      case (MotionEvent.ACTION_DOWN):
        if (tary >= (mViewHeight-mCardHeight)) {
          if (mSelectedCard == -1) {
            mSelectedCard = findTargetHand();
          }
        } else if (tarx > draw.left && tarx < draw.right && tary > draw.top && tary < draw.bottom) {
          if (mWinner == -1 && (mPlayerCount > 1 || (mPlayerCount == 1 && mTurn == 0))) {
            mSelectedCard = -1;
            mSelectedPile = -1;
            nextTurn();
          }
        } else {
          mSelectedCard = -1;
          mSelectedPile = -1;
          findTargetPile();
        }
      break;
      case MotionEvent.ACTION_MOVE:
        if (mSelectedCard >= 0) {
          if (mHighlightCards) {
            highlightPile();
          }
        } else if (mSelectedPile >= 0 && mSides[mSelectedPile] != null) {
          if (mHighlightCards){
            highlightPile();
          }
        }
        break;
      case MotionEvent.ACTION_UP:
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          findTargetPile();
        }
        mSelectedCard = -1;
        mSelectedPile = -1;
        mHighlightedPile = -1;
        for (int i = 0; i < 4; i++) mCorners[i].clearCorner();
        playerWin();
        break;
      }
      mTable.postInvalidate();
    }
    return true;
  }

  
  // User preferences methods

  /** Updates all user preferences */
  public void updatePrefs() {
    // Normal preferences
    mDrawPileCount = mPrefs.getBoolean(mContext.getResources().getString(R.string.pref_key_drawPileCount), false);
    mComputerDelay = mPrefs.getInt(mContext.getResources().getString(R.string.pref_key_computerDelay), 1000);
    mHighlightCards = mPrefs.getBoolean(mContext.getResources().getString(R.string.pref_key_highlightCards), true);
    mAutosave = mPrefs.getBoolean(mContext.getResources().getString(R.string.pref_key_autosave), false);
    mEmptyDeckWarning = mPrefs.getBoolean(mContext.getResources().getString(R.string.pref_key_emptyDeckWarning), true);
    mSortHand = mPrefs.getBoolean(mContext.getResources().getString(R.string.pref_key_sortHand), false);
    // Cheats
    mShowComputerHand = mPrefs.getBoolean(mContext.getResources().getString(R.string.pref_key_showComputerHand), false);
  }

  // Private methods
  /** Restore a previously saved game */
  private boolean restoreGame() {
    try {
      FileInputStream fip = mContext.openFileInput(mSaveString);
      ObjectInputStream in = new ObjectInputStream(fip);
      mTurn = in.readInt();
      
      mHands[0] = new Hand();
      int playerCount = in.readInt();
      Card c;
      for (int i = 0; i < playerCount; i++) {
        c = (Card) in.readObject();
        if (c != null) c.setImage(mContext, mCardStyle);
        mHands[0].addCard(c);
      }
      
      int computerCount = in.readInt();
      mHands[1] = new Hand();
      for (int i = 0; i < computerCount; i++) {
        c = (Card) in.readObject();
        if (c != null) c.setImage(mContext, mCardStyle);
        mHands[1].addCard(c);
      }

      int deckLeft = in.readInt();      
      for (int i = 0; i < 52; i++) {
        mDeck.setCardAt(i, (Card)in.readObject());
      }
      mDeck.setCardsUsed(52-deckLeft);
      
      for (int i = 0; i < 4; i++) {
        mSides[i] = (Pile)in.readObject();
        mSides[i].setImages(mContext, mCardStyle);
        mSides[i].rot = i;
      }
      for (int i = 0; i < 4; i++) {
        mCorners[i] = (Pile)in.readObject();
        mCorners[i].setImages(mContext, mCardStyle);
        mCorners[i].rot = i+4;
      }
      
      mCanUndo = in.readBoolean();
      mUndoCard = (Card)in.readObject();
      if (mUndoCard != null) mUndoCard.setImage(mContext, mCardStyle);
      mReplaceWithCard = (Card)in.readObject();
      if (mReplaceWithCard != null) mReplaceWithCard.setImage(mContext, mCardStyle);
      mUndoPos = in.readInt();
      mUndoIsSide = in.readBoolean();
      
      mContext.deleteFile(mSaveString);
      in.close();
      fip.close();
    } catch (FileNotFoundException e) {
      mTable.post(new Runnable() {
        @Override
        public void run() {
          Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_fileNotFoundException), Toast.LENGTH_SHORT).show();
        }
      });
      return false;
    } catch (StreamCorruptedException e) {
      mTable.post(new Runnable() {
        @Override
        public void run() {
          Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_streamCorruptedException), Toast.LENGTH_SHORT).show();
        }
      });
      return false;
    } catch (IOException e) {
      mTable.post(new Runnable() {
        @Override
        public void run() {
          Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_IOException), Toast.LENGTH_SHORT).show();
        }
      });
      return false;
    } catch (ClassNotFoundException e) {
      mTable.post(new Runnable() {
        @Override
        public void run() {
          Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_classNotFoundException), Toast.LENGTH_SHORT).show();
        }
      });
      return false;
    }
    return true;
  }

  private void drawHand(Canvas c) {
    int turn = (mPlayerCount == 1 && mTurn == 1) ? 0 : mTurn;
    int x = 0;
    int xmax = 0;
    int y = mViewHeight - mCardHeight;
    int center = mViewWidth/2;
    int count = mHands[turn].getCardCount();
    if (count == 1) {
      if (mHands[turn].getCard(0) != null && mSelectedCard != 0) {
        c.drawBitmap(mHands[turn].getCard(0).getImage(), center-(mCardWidth/2), y, null);
        mHands[turn].getCard(0).setX(center-(mCardWidth/2));
        mHands[turn].getCard(0).setXMax(center-(mCardWidth/2)+mCardWidth);
      }
    } else if (count == 2) {
      if (mHands[turn].getCard(0) != null && mSelectedCard != 0) {
        c.drawBitmap(mHands[turn].getCard(0).getImage(), center-mCardWidth-5, y, null);
        mHands[turn].getCard(0).setX(center-mCardWidth-5);
        mHands[turn].getCard(0).setXMax(center-5);
      }
      if (mHands[turn].getCard(1) != null && mSelectedCard != 1) {
        c.drawBitmap(mHands[turn].getCard(1).getImage(), center+5, y, null);
        mHands[turn].getCard(1).setX(center+5);
        mHands[turn].getCard(1).setXMax(center+5+mCardWidth);
      }
    } else if (count == 3) {
      if (mHands[turn].getCard(0) != null && mSelectedCard != 0) {
        c.drawBitmap(mHands[turn].getCard(0).getImage(), center-(mCardWidth/2)-mCardWidth-10, y, null);
        mHands[turn].getCard(0).setX(center-(mCardWidth/2)-mCardWidth-10);
        mHands[turn].getCard(0).setXMax(center-(mCardWidth/2)-10);
      }
      if (mHands[turn].getCard(1) != null && mSelectedCard != 1) {
        c.drawBitmap(mHands[turn].getCard(1).getImage(), center-(mCardWidth/2), y, null);
        mHands[turn].getCard(1).setX(center-(mCardWidth/2));
        mHands[turn].getCard(1).setXMax(center-(mCardWidth/2)+mCardWidth);
      }
      if (mHands[turn].getCard(2) != null && mSelectedCard != 2) {
        c.drawBitmap(mHands[turn].getCard(2).getImage(), center+(mCardWidth/2)+10, y, null);
        mHands[turn].getCard(2).setX(center+(mCardWidth/2)+10);
        mHands[turn].getCard(2).setXMax(center+(mCardWidth/2)+10+mCardWidth);
      }
    } else if (count == 4) {
      if (mHands[turn].getCard(0) != null && mSelectedCard != 0) {
        c.drawBitmap(mHands[turn].getCard(0).getImage(), center-(mCardWidth*2)-15, y, null);
        mHands[turn].getCard(0).setX(center-(mCardWidth*2)-15);
        mHands[turn].getCard(0).setXMax(center-mCardWidth-15);
      }
      if (mHands[turn].getCard(1) != null && mSelectedCard != 1) {
        c.drawBitmap(mHands[turn].getCard(1).getImage(), center-mCardWidth-5, y, null);
        mHands[turn].getCard(1).setX(center-mCardWidth-5);
        mHands[turn].getCard(1).setXMax(center-5);
      }
      if (mHands[turn].getCard(2) != null && mSelectedCard != 2) {
        c.drawBitmap(mHands[turn].getCard(2).getImage(), center+5, y, null);
        mHands[turn].getCard(2).setX(center+5);
        mHands[turn].getCard(2).setXMax(center+5+mCardWidth);
      }
      if (mHands[turn].getCard(3) != null && mSelectedCard != 3) {
        c.drawBitmap(mHands[turn].getCard(3).getImage(), center+mCardWidth+15, y, null);
        mHands[turn].getCard(3).setX(center+mCardWidth+15);
        mHands[turn].getCard(3).setXMax(center+mCardWidth+15+mCardWidth);
      }
    } else {
      Card card;
      for (int i = 0; i < count; i++) {
        card = mHands[turn].getCard(i);
        x = ( (i * ((mViewWidth-mCardWidth)/(count-1))) );
        if (i < count-1) {
          xmax = ( ((i+1) * ((mViewWidth-mCardWidth)/(count-1))) );
        } else {
          xmax = x+mCardWidth;
        }
        if (card != null && mSelectedCard != i) {
          card.setX(x);
          card.setY(y);
          card.setXMax(xmax);
          c.drawBitmap(card.getImage(), x, y, null);
        }
      }
    }
  }

  private void drawComputerHand(Canvas c) {
    int count = mHands[1].getCardCount();
    int y = -(mCardHeight/2);
    int x = 0;
    int center = mViewWidth/2;
    if (mShowComputerHand) {
      for (int i = 0; i < count; i++) {
        if (count > 1) {
          x = ( (i * ((mViewWidth-mCardWidth)/(count-1))) );
          c.drawBitmap(mHands[1].getCard(i).getImage(), x, y, null);
        } else {
          c.drawBitmap(mHands[1].getCard(0).getImage(), center-(mCardWidth/2), y, null);
        }
      }
    }
    else {
      if (count == 1) {
        c.drawBitmap(mCardBack, center-(mCardWidth/2), y, null);
      } else if (count == 2) {
        c.drawBitmap(mCardBack, center-mCardWidth-5, y, null);
        c.drawBitmap(mCardBack, center+5, y, null);
      } else if (count == 3) {
        c.drawBitmap(mCardBack, center-(mCardWidth/2)-mCardWidth-10, y, null);
        c.drawBitmap(mCardBack, center-(mCardWidth/2), y, null);
        c.drawBitmap(mCardBack, center+(mCardWidth/2)+10, y, null);
      } else if (count == 4) {
        c.drawBitmap(mCardBack, center-(mCardWidth*2)-15, y, null);
        c.drawBitmap(mCardBack, center-mCardWidth-5, y, null);
        c.drawBitmap(mCardBack, center+5, y, null);
        c.drawBitmap(mCardBack, center+mCardWidth+15, y, null);
      } else {
        for (int i = 0; i < count; i++) {
          x = ( (i * ((mViewWidth-mCardWidth)/(count-1))) );
          c.drawBitmap(mCardBack, x, y, null);
        }
      }
    }
  }
  
  private void drawHighlighted(Canvas c) {
    if (mHighlightedPile < 0) return;  
    // See if it is a corner or side to highlight
    if (mHighlightedPile <= 3) {
      // Highlight side
      if (mSelectedCard >= 0 && !mSides[mHighlightedPile].playable(mHands[mTurn].getCard(mSelectedCard))) return;    
      else if (mSelectedPile >= 0 && !mSides[mSelectedPile].playable(mSides[mHighlightedPile])) return;
      
      int rot = mSides[mHighlightedPile].rot;
      
      if (rot == Pile.UP || rot == Pile.DOWN) mSides[mHighlightedPile].drawHighlighted(c, mGlowNormal, mCardHeight);
      else if (rot == Pile.LEFT || rot == Pile.RIGHT) mSides[mHighlightedPile].drawHighlighted(c, mGlowSide, mCardHeight);
      
    } else {
      // Highlight corner
      if (mSelectedCard >= 0 && !mCorners[mHighlightedPile-4].playable(mHands[mTurn].getCard(mSelectedCard))) return;    
      else if (mSelectedPile >= 0 && !mSides[mSelectedPile].playable(mCorners[mHighlightedPile-4])) return;
      
      int rot = mCorners[mHighlightedPile-4].rot;
      
      if (rot == Pile.UP_LEFT || rot == Pile.DOWN_RIGHT) mCorners[mHighlightedPile-4].drawHighlighted(c, mGlowCorner1, mCardHeight);
      else if (rot == Pile.UP_RIGHT || rot == Pile.DOWN_LEFT) mCorners[mHighlightedPile-4].drawHighlighted(c, mGlowCorner2, mCardHeight);
    }
    
  }

  private int findTargetHand() {
    int cards = mHands[mTurn].getCardCount();
    for (int i = 0; i < cards; i++) {
      Card c = mHands[mTurn].getCard(i);
      int cx = c.getX();
      int mx = c.getXMax();
      if ( ((i == cards-1) && tarx >= cx && tarx <= cx + mCardWidth) || (tarx >= cx && tarx <= mx)) { 
        return i;
      }
    }
    return -1;
  }

  private void findTargetPile() {
    // Check sides
    for (int i = 0; i < mSides.length; i++) {
      if (tarx >= mSides[i].pos.left-(mCardWidth/2) && tarx <= mSides[i].pos.right+(mCardWidth/2) &&
          tary >= mSides[i].pos.top-(mCardHeight/2) && tary <= mSides[i].pos.bottom+(mCardHeight/4)) {
        if (mSelectedCard >= 0) {
          playCard(i);
        } else if (mSelectedPile != -1) {
          movePile(i);
        } else if (mSides[i] != null){
          mSelectedPile = i;
        }
      }
    }
    
    // Check corners
    if (tarx >= mSides[0].pos.left && tarx <= mSides[1].pos.left-5) {
      if (tary >= mSides[1].pos.top && tary <= mSides[0].pos.top-5) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          playCorner(0);
        }
      }
      else if (tary >= mSides[0].pos.bottom+5 && tary <= mSides[3].pos.bottom) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          playCorner(3);
        }
      }
    } else if (tarx >= mSides[1].pos.right+5 && tarx <= mSides[2].pos.right) {
      if (tary >= mSides[1].pos.top-5 && tary <= mSides[2].pos.top) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          playCorner(1);
        }
      } else if (tary >= mSides[2].pos.bottom+5 && tary <= mSides[3].pos.bottom) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          playCorner(2);
        }
      }
    }
  }

  private void movePile(int dest) {
    if (mSides[mSelectedPile].moveTo(mSides[dest])) {
      mCanUndo = false;
    }
  }

  private void playCard(int dest) {
    if (mSelectedCard >= 0) {
      mUndoCard = mHands[mTurn].getCard(mSelectedCard);
      mReplaceWithCard = mSides[dest].last;
      if (mSides[dest].play(mHands[mTurn].getCard(mSelectedCard))) {
        mHands[mTurn].removeCard(mSelectedCard);
        mCanUndo = true;
        mUndoIsSide = true;
        mUndoPos = dest;
      }

      for (int i = 0; i < mSides.length; i++) {
        if (mSides[i].first == null) {
          if (mSides[dest].playUnder(mHands[mTurn].getCard(mSelectedCard))) {
            mHands[mTurn].removeCard(mSelectedCard);
            mCanUndo = false;
            break;
          }
        }
      }
    }
  }

  private void playCorner(int dest) {
    if (mSelectedCard >= 0){
      mUndoCard = mHands[mTurn].getCard(mSelectedCard);
      mReplaceWithCard = mCorners[dest].last;
      if (mCorners[dest].play(mHands[mTurn].getCard(mSelectedCard))) {
        mHands[mTurn].removeCard(mSelectedCard);
        mCanUndo = true;
        mUndoIsSide = false;
        mUndoPos = dest;
      }
    } else if (mSelectedPile >= 0) {
      if (mSides[mSelectedPile].moveTo(mCorners[dest])) {
        mCanUndo = false;
      }
    }
  }

  private void highlightPile() {
    // Check sides
    mHighlightedPile = -1;
    for (int i = 0; i < mSides.length; i++) {
      if (tarx >= mSides[i].pos.left-(mCardWidth/2) && tarx <= mSides[i].pos.right+(mCardWidth/2) &&
          tary >= mSides[i].pos.top-(mCardHeight/2) && tary <= mSides[i].pos.bottom+(mCardHeight/4)) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          mHighlightedPile = i;
        }
      }
    }
    if (mHighlightedPile >= 0) return; // If a side is highlighted, don't try to highlight a corner
    // Check corners
    if (tarx >= mSides[0].pos.left && tarx <= mSides[1].pos.left-5) {
      if (tary >= mSides[1].pos.top && tary <= mSides[0].pos.top-5) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          mHighlightedPile = 4;
        }
      }
      else if (tary >= mSides[0].pos.bottom+5 && tary <= mSides[3].pos.bottom) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          mHighlightedPile = 7;
        }
      }
    } else if (tarx >= mSides[1].pos.right+5 && tarx <= mSides[2].pos.right) {
      if (tary >= mSides[1].pos.top-5 && tary <= mSides[2].pos.top) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          mHighlightedPile = 5;
        }
      } else if (tary >= mSides[2].pos.bottom+5 && tary <= mSides[3].pos.bottom) {
        if (mSelectedCard >= 0 || mSelectedPile >= 0) {
          mHighlightedPile = 6;
        }
      }
    }
  }

  /** Check if the computer has won */
  private boolean compWin() {
    mTable.postInvalidate();
    if (mPlayerCount == 1 && mHands[1].getCardCount() == 0) {
      if (mUsername != null && !mUsername.equals(mContext.getResources().getString(R.string.username_none)) && !mUsername.equals("")) {
        try {
          FileInputStream fis = mContext.openFileInput(mUsername);
          ObjectInputStream in = new ObjectInputStream(fis);
          Player p = (Player)in.readObject();
          in.close();
          fis.close();

          p.Lose();

          FileOutputStream fop = mContext.openFileOutput(mUsername, Context.MODE_PRIVATE);
          ObjectOutputStream out = new ObjectOutputStream(fop);
          out.writeObject(p);
          out.close();
          fop.close();
        } catch (FileNotFoundException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_fileNotFoundException), Toast.LENGTH_SHORT).show();
            }
          });
        } catch (StreamCorruptedException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_streamCorruptedException), Toast.LENGTH_SHORT).show();
            }
          });
        } catch (IOException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_IOException), Toast.LENGTH_SHORT).show();
            }
          });
        } catch (ClassNotFoundException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_classNotFoundException), Toast.LENGTH_SHORT).show();
            }
          });
        }
      }
      mCanUndo = false;
      mWinner = 1;
      setChanged();
      notifyObservers();
      mTable.postInvalidate();
      return true;
    }
    return false;
  }

  /** Check if the current player has won */  
  private boolean playerWin() {
    if ((mPlayerCount > 1 && mHands[mTurn].getCardCount() == 0) || (mPlayerCount == 1 && mHands[0].getCardCount() == 0)) {
      if (mPlayerCount == 1 && mUsername != mContext.getResources().getString(R.string.username_none) && mUsername != null && !mUsername.equals("")) {
        try {
          FileInputStream fis = mContext.openFileInput(mUsername);
          ObjectInputStream in = new ObjectInputStream(fis);
          Player p = (Player)in.readObject();
          in.close();
          fis.close();

          p.Win();

          FileOutputStream fop = mContext.openFileOutput(mUsername, Context.MODE_PRIVATE);
          ObjectOutputStream out = new ObjectOutputStream(fop);
          out.writeObject(p);
          out.close();
          fop.close();
        } catch (FileNotFoundException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_fileNotFoundException), Toast.LENGTH_SHORT).show();
            }
          });
        } catch (StreamCorruptedException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_streamCorruptedException), Toast.LENGTH_SHORT).show();
            }
          });
        } catch (IOException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_IOException), Toast.LENGTH_SHORT).show();
            }
          });
        } catch (ClassNotFoundException e) {
          mTable.post(new Runnable() {
            @Override
            public void run() {
              Toast.makeText(mContext, mContext.getResources().getString(R.string.toast_classNotFoundException), Toast.LENGTH_SHORT).show();
            }
          });
        }
      }
      mCanUndo = false;
      mWinner = mTurn;
      setChanged();
      notifyObservers();
      mTable.postInvalidate();
      return true;
    }
    return false;
  }

  /** Null values to ensure they get picked up by Garbage Collector **/
  public void freeUp() {
    mPrefs.edit().putBoolean(mContext.getResources().getString(R.string.pref_key_inGame), false).commit();
    mUndoCard = null;
    mReplaceWithCard = null;
    for (int i = 0; i < mHands.length; i++) mHands[i] = null;  
    for (int i = 0; i < mSides.length; i++) mSides[i] = null;
    for (int i = 0; i < mCorners.length; i++) mCorners[i] = null;
    mDeck = null;
    mSides = null;
    mCorners = null;
    if (mPlayerCount > 1) {
      for (int i = 0; i < mPlayerBitmaps.length; i++) {
        if (mPlayerBitmaps[i] != null) mPlayerBitmaps[i].recycle();
      }
    }
    mGlowNormal.recycle();
    mGlowSide.recycle();
    mGlowCorner1.recycle();
    mGlowCorner2.recycle();
    mCardBack.recycle();
    mContext = null;
    mTable = null;    
    System.gc();
  }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.