Midterm Progress – Almost a Card Game

So, we have to make a game for our midterm. The first thing that came to my mind was the long-missed card game I used to play with my family. That game is a 5 player game that would have become very difficult to cod in the limited time and also go beyond the scope of the assignment. Hence, I decided to adapt that game to my version of a 2-player game (somewhat similar to an already existing card game called ‘Honeymoon Bridge’). I get so excited by even just hearing the word ‘cards’, hence there was no better choice for me for this project.

Now to simplify what the game is (as many people aren’t familiar with tricks-making card games), here is an outline –

  •  There will be a normal playing deck of 52 cards, however, only 26 cards will be dealt.
  • Each player gets 13 cards and the aim of the game is to make the most tricks.
  • None of the players know which are are still in the deck / have not been dealt so a lot of strategy and some guesswork is required to win the game.
  • Now, what does it mean to make a trick? Both players play a card one by one. The bigger numbered card gets the trick (King is the highest).
    • There are a few rules as to what cards you can play. The first player can put any card, but everyone else on the table has to play a card of the same suit. In case you do not have any cards left of that suit, you may play any other suit but you cannot get the trick in that round.
    • The player who plays first is decided by who makes the trick. For the first round, the person who is dealt first plays first.
  • Since each player has 13 cards, there will be 13 rounds and any player needs a minimum of 7 tricks to win the game.
  • Once 7 tricks have been made, the game finishes.

Besides figuring out the logic, I also had to keep in mind how visually my game arena looks and how I envision my cards to move along the screen to give a sense of an actual card game. I started with first planning out and sketching all my idea (see one of my many pages below), going over pseudocode, and then finally started programming it in Processing.

I first made placeholders for where all I wanted my cards, deck, playing table, etc. to be.

Once the cards and deck were placed, it was time to deal cards. I was going for a theme where one could see each card being dealt. Initially, it was a big disaster (see video below). I was unable to control the position or the timing of the cards being dealt. After working for about 3 hours on it, I decided to leave it for a while and then come back to it.

If not physically, I thought it is a good idea to focus on logically dealing the cards. For that, I made 2 arrays – p1Hand and p2Hand – to keep track of card IDs that have been assigned to each player. I used random() to assign the cards and a boolean variable dealt to keep track of if a card has been dealt or not (so as to not repeat a card while dealing). Once the cards had been dealt ‘logically’, I made a sorting function to arrange the cards by suit and number because you cannot play a good card game without having arranged the cards in hand properly! I used Bubble Sort to do this (which I knew from one of my previous CS courses).

Cards Arranged By Suit

After this, I tried again to deal the cards ‘physically’ on the screen and surprisingly succeeded.  I got to it by going step by step and building a really complicated code, which I hope to simplify if I am able to think of a better alternative. Though, having the cards deal exactly how I wanted them to, gave me a lot of satisfaction. This was the hardest part till now.

Then I made placeholders for where the cards had to be played on the playing table and just tried coding for the cards to show up there with a click.

Once that worked properly, I tried going further and coding this according to game rules (being able to click on certain cards or not according to the game rules and keeping track of whose turn it is). Also, after both players have played their cards, the round counter increments, and I call the trick() function to check who won the trick and calculate the total number of tricks of each person.

This is my progress as of now (better to watch the video on 2x speed! :D) –

It contains a few bugs that I still need to fix –

  • The tricks are not incrementing all the time. There is some error in that which I have to look at
  • Not all cards are being shown on top

Things I still need to work on –

  • Change the turn based on who makes the trick
  • After making a trick, the cards need to go on the right side and stack up like you put tricks in a line (this will again require animation)
  • Rounds need to be updated properly
  • Appropriate delays need to be made in order to see the movements on the board
  • Decide whether PC is the second player or have 2 people play it – and accordingly code it
  • Add sounds of dealing, playing, stacking to make it more realistic
  • Make the menu and instructions page
  • Make the end page with a restart option
  • Introduce the trump variation if time left
  • Introduce the ‘baazi’ variation if time left
  • (and anything else that I am currently missing)

CODE

The Card class:

class Card
{
  int id; /* rom 1 to 52 - 
           first 13 = clubs, then diamonds, heart and last 13 spades
           - according to heirarchy of suits */
  int number;
  String suit;
  PImage cImg;
  float posX, posY, prevPosX, prevPosY;
  boolean dealt; //it the card has been assignmed to any player
  boolean visible; //a card is visible only in the hand or table
  boolean played; //to keep track whether the card has been played by a player or not
  
  //constructor - get parameters of photo, number and suit
  Card (int cid, int num, String s, PImage img)
  {
    id = cid;
    number = num;
    suit = s;
    cImg = img;
    dealt = false;
    visible = false;
    played = false;
  }
 
}

The main code:

/* 
 SOME BASIC GAME RULES
 A deck of 52 cards
 Only 26 cards are dealt - 13 per player
 Each player plays one card, the one who has a higher card winds the trick  - this is one round
 the game has 13 rounds
 so whoever makes 7 tricks first has more tricks and thus has won the game
 the player who plays his card second can only play a card of the same suit unless they don't have that suit
 the player who makes the trick plays first in the enxt round
 */

int screenMode = 1;

//loading all images
PImage[] bg;
PImage[] cImgs;
PImage table;

//Deck deck;
Card[] cards;
int[] p1Hand; //keeps track of the cards in hand for player one (by id)
int[] p2Hand; //keeps track of the cards in hand for player two (by id)
//Card[] back; - can have this seperately

//game screen positions
float cardW, deckW, pCardW;
float ratio;
float tableH, tableW; //dimension of the playing table
float marginX, marginY; // to keep track of our=tside table margins
float p1X, p2X; //to keep track of position where players play their cards
float deckPosX, deckPosY;

//variables needed while dealing the cards
boolean dealt = false;
int dealCardNum = 0; //to keep track of which card is being dealt;
float dealIncNum = 5; //how much distance to cover for each dealt card
int[] ctr; //to keep track how much distance my dealt card has travelled

//variables needed while playing
int round = 0; //game consists of total 13 rounds (each player has 13 cards)
int turn = 1; //to keep track which player's turn it is
int firstTurn = 1; //to keep track of which player has to start round - no restriction on playing the card
int card1Played; //to keep track of which card was played first - to match the suit
int p1trick = 0; //to keep track of the number of tricks made by player 1
int p2trick = 0; //to keep track of the number of tricks made by player 2

void setup()
{
  //canvas size
  size(1200, 800);
  //fullScreen();
  background(255);
  //frameRate(180);

  //setting the various positions of game screen
  tableW = width*0.6;
  tableH = height*0.55;
  marginX = (width-tableW)/2;
  marginY = (height-tableH)/2;
  cardW = (tableW+marginX)/13; //for cards in hand
  ratio = 1056/691; //to determine height of cards depending on cardW
  deckW = marginX*0.6; //for cards in the deck
  p1X = width/2 - deckW;
  p2X = width/2 + deckW;
  deckPosX = marginX/2;
  deckPosY = height/2;

  rectMode(CENTER);
  imageMode(CENTER);
  textAlign(CENTER, CENTER);

  //loading backgrounds for different game screens
  bg = new PImage[4];
  bg[1] = loadImage("bg1.jpg");

  //loading additional images
  table = loadImage("table.jpg");

  //loading card images
  cImgs = new PImage[53]; // 1 extra for back side
  // to store card image names
  String[] filenames = {"back.png", "AC.png", "2C.png", "3C.png", "4C.png", "5C.png", "6C.png", 
    "7C.png", "8C.png", "9C.png", "10C.png", "JC.png", "QC.png", "KC.png", 
    "AD.png", "2D.png", "3D.png", "4D.png", "5D.png", "6D.png", "7D.png", 
    "8D.png", "9D.png", "10D.png", "JD.png", "QD.png", "KD.png", 
    "AH.png", "2H.png", "3H.png", "4H.png", "5H.png", "6H.png", "7H.png", 
    "8H.png", "9H.png", "10H.png", "JH.png", "QH.png", "KH.png", 
    "AS.png", "2S.png", "3S.png", "4S.png", "5S.png", "6S.png", "7S.png", 
    "8S.png", "9S.png", "10S.png", "JS.png", "QS.png", "KS.png" };
  for (int i=0; i<=52; i++)
    cImgs[i] = loadImage(filenames[i]);

  //creating my card objects
  cards = new Card[53];
  cards[0] = new Card(0, 0, "back", cImgs[0]);
  //for clubs - cards 1-13
  for (int i=1; i<=13; i++)
    cards[i] = new Card(i, i, "clubs", cImgs[i]);
  //for diamonds - cards 14-26
  for (int i=14; i<=26; i++)
    cards[i] = new Card(i, i-13, "diamonds", cImgs[i]);
  //for hearts - cards 27-39
  for (int i=27; i<=39; i++)
    cards[i] = new Card(i, i-26, "hearts", cImgs[i]);
  //for spades - cards 40-52
  for (int i=40; i<=52; i++)
    cards[i] = new Card(i, i-39, "spades", cImgs[i]);

  //player hands
  p1Hand = new int[13];
  p2Hand = new int[13];

  //deal
  ctr = new int[26];
  for (int i=0; i<26; i++)
    ctr[i]=0;
  deal();
}

void draw()
{
  switch (screenMode)
  {
  case 0 : 
    startScreen(); 
    break;
  case 1 : 
    gameScreen(); 
    break;
  case 2 : 
    exitScreen(); 
    break;
  case 3 : 
    instScreen(); 
    break; //instruction screen
  }
}

void startScreen()
{
}

void gameScreen()
{
  //background - red texture
  image(bg[1], width/2, height/2, width, height);
  //display the table
  pushStyle();
  strokeWeight(10);
  fill(0);
  stroke(0, 120, 70);
  rect(width/2, height/2, tableW, tableH);
  popStyle();
  image(table, width/2, height/2, tableW, tableH);

  //displaying deck cards
  //for (int i = 0; i<26; i++)
  image(cards[0].cImg, marginX/2, height/2, deckW, deckW/691*1056);
  pushStyle();
  fill(255, 255, 0);
  textSize(18);
  text("Deck", marginX/2, height/2-deckW/691*1056*1.2/2);
  popStyle();

  //displaying other placeholders
  pushStyle();
  fill(0, 0, 0, 160);
  rect(p1X, height/2, deckW, deckW/691*1056);
  rect(p2X, height/2, deckW, deckW/691*1056);
  fill(255, 255, 0);
  textSize(16);
  text("Player 1 Card", p1X, height/2-deckW/691*1056*1.2/2);
  text("Player 2 Card", p2X, height/2-deckW/691*1056*1.2/2);
  popStyle();

  //dealing cards
  if (!dealt)
  {
    for (int i=0; i<26; i++)
      if (ctr[i]>dealIncNum)
        if (i%2==0)
          image(cards[0].cImg, marginX/2+cardW/2+i/2*cardW, height-marginY/2, cardW, cardW/691*1056);
        else
          image(cards[0].cImg, marginX/2+cardW/2+i/2*cardW, marginY/2, cardW, cardW/691*1056);
    if (dealCardNum%2 == 0)
      dealCards(marginX/2+cardW/2+dealCardNum/2*cardW, height-marginY/2);
    else 
    dealCards(marginX/2+cardW/2+dealCardNum/2*cardW, marginY/2);
    if (dealCardNum >= 26)
      dealt = true;
  } 

  //start game after dealing cards
  else 
  {
    //play cards in the center
    //println(mouseX + " " + mouseY);

    //if it is Player 1's turn
    if (turn == 1)
    {
      if (firstTurn == 1) //player 1 is starting the round
      {
        if (mouseX<width-marginX/2 && mouseX>marginX/2 && 
          mouseY<height-marginY/2+cardW/691*1056/2 && mouseY>height-marginY/2-cardW/691*1056/2 
          && mousePressed) 
        {
          //background(255);
          int cardPlayed;
          cardPlayed = int((mouseX-marginX/2)/cardW); 
          card1Played = p1Hand[cardPlayed];
          cards[card1Played].posX=p1X;
          cards[card1Played].posY=height/2;
          cards[card1Played].played = true;
          turn=2; //player 2's turn now
          //image(cards[p1Hand[cardPlayed]].cImg, cards[p1Hand[cardPlayed]].posX, cards[p1Hand[cardPlayed]].posY, cardW, cardW/691*1056);
        }
      }
    }

    if (turn == 2)
    {
      if (firstTurn == 2) //player 2 is starting the round
      {
        if (mouseX<width-marginX/2 && mouseX>marginX/2 && 
          mouseY<marginY/2+cardW/691*1056/2 && mouseY>marginY/2-cardW/691*1056/2 
          && mousePressed) 
        {
          //background(255);
          int cardPlayed; //gives index of card in hand not cards array
          cardPlayed = int((mouseX-marginX/2)/cardW); 
          card1Played = p2Hand[cardPlayed];
          cards[card1Played].posX=p2X;
          cards[card1Played].posY=height/2;
          cards[card1Played].played = true;
          turn=1; //player 2's turn now
          //image(cards[p1Hand[cardPlayed]].cImg, cards[p1Hand[cardPlayed]].posX, cards[p1Hand[cardPlayed]].posY, cardW, cardW/691*1056);
        }
      } else //if the player is second to play in this round
      {
        if (mouseX<width-marginX/2 && mouseX>marginX/2 && 
          mouseY<marginY/2+cardW/691*1056/2 && mouseY>marginY/2-cardW/691*1056/2 
          && mousePressed)
        {
          int cardPlayed; //gives index of card in hand not cards array
          cardPlayed = int((mouseX-marginX/2)/cardW);
          if (check(p2Hand[cardPlayed], p2Hand)) //to check if the card played is correct or not;
          {
            //updating position of the card
            cards[p2Hand[cardPlayed]].posX=p2X;
            cards[p2Hand[cardPlayed]].posY=height/2;
            cards[p2Hand[cardPlayed]].played = true;

            //see who won the trick
            trick(p2Hand[cardPlayed], 2);
            round++;
            cards[card1Played].visible = false;
            cards[p2Hand[cardPlayed]].visible = false;
            turn = 1; //needs to be changed according to who made the trick
          }
        }
      }
    }

    //display all the cards in their respective places
    for (int i=1; i<=52; i++)
      if (cards[i].visible)
        image(cards[i].cImg, cards[i].posX, cards[i].posY, cardW, cardW/691*1056);
        
  pushStyle();
  fill(255, 255, 0);
  textSize(18);
  text(p1trick, width-marginX/2, height/2+deckW/691*1056*1.2/2);
  text(p2trick, width-marginX/2, height/2-deckW/691*1056*1.2/2);
  popStyle();

    //loop();
  }
}


void dealCards(float x, float y)
{
  if (ctr[dealCardNum]<=dealIncNum)
  {
    float incrementX = (x-deckPosX)/dealIncNum;
    float incrementY = (y-deckPosY)/dealIncNum;
    float incrementDeckW = (cardW-deckW)/dealIncNum;
    //x = marginX/2+cardW/2+0*cardW;
    //y = height-marginY/2;
    //dealPosXi += (x - dealPosXi)*0.5;
    //dealPosYi += (y - dealPosYi)*0.5;
    //dealDeckW += (cardW - dealDeckW)0.5;
    image(cards[0].cImg, deckPosX+ctr[dealCardNum]*incrementX, 
      deckPosY+ctr[dealCardNum]*incrementY, 
      deckW+ctr[dealCardNum]*incrementDeckW, 
      (deckW+ctr[dealCardNum]*incrementDeckW)/691*1056);
    ctr[dealCardNum]++;
  } else
  {
    //image(cards[0].cImg, x, y, cardW, cardW/691*1056);
    dealCardNum++;
  }
}

void sortCards(int[] hand) //using bubble sort - I know this from my prev CS course
{
  int n = hand.length;
  for (int i = 0; i < n - 1; i++) 
  {
    for (int j = 0; j < n - i - 1; j++) 
    {
      if (hand[j] > hand[j+1]) 
      {
        //swap the elements
        int temp = hand[j];
        hand[j] = hand[j+1];
        hand[j+1] = temp;
      }
    }
  }
}


boolean check(int index, int[] hand)
{
  if (cards[index].suit == cards[card1Played].suit)
    return true; //correct card has been played
  else
  {
    //first check if there are cards of this suit
    for (int i=0; i<13; i++)
      if (cards[hand[i]].suit == cards[card1Played].suit && !cards[hand[i]].played) //found a card of same suit
      {
        text("You must play a card of the same suit", width/2, marginY*1.1);       
        return false;
      }
    return true;
  }
}

void trick(int card2, int player)
{
  int card1 = card1Played;
  if (cards[card1].suit == cards[card2].suit)
  {
    if (card2 > card1)
    {
      if (player == 1) //first player played a greater card
        p1trick++;
      else if (player == 2)
        p2trick++;
    }
    else //if the second card played is smaller
    {
      if (player == 1) //first player played a greater card
        p2trick++;
      else if (player == 2)
        p1trick++;
    }
  } 
  else
  {
    if (player == 1)
      p2trick++;
    else
      p1trick++;
  }
}

void exitScreen()
{
}

//instruction screen
void instScreen()
{
}

void deal()
{
  //assigning cards to each player / logically dealing cards
  int cid;
  for (int i=0; i<13; i++)
  {
    do {
      cid = int(random(52))+1;
    } while (cards[cid].dealt);
    if (!cards[cid].dealt)
    {
      cards[cid].dealt = true;
      p1Hand[i]=cid; //player 1 has been assigned/dealt this card
      //println(cid);
    }

    do {
      cid = int(random(52))+1;
    } while (cards[cid].dealt);
    if (!cards[cid].dealt)
    {
      cards[cid].dealt = true;
      p2Hand[i]=cid; //player 2 has been assigned/dealt this card
      //println(cid);
    }
  }

  //sort the cards in hand - because you cannot play until you arrange cards properly
  sortCards(p1Hand);
  sortCards(p2Hand);

  //displaying hand cards for player 1 that have been assigned/dealt
  int j;
  for (int i = 0; i<13; i++) {
    j = p1Hand[i];
    //update position in the class attribute for those cards
    cards[j].posX = marginX/2+cardW/2+i*cardW;
    cards[j].posY = height-marginY/2;
    cards[j].visible = true;
    //image(cards[j].cImg, cards[j].posX, cards[j].posY, cardW, cardW/691*1056);
    //fill(0,0,255);
    //tresting if values have been assignmed properly
    //text(p1Hand[i], marginX/2+cardW/2+i*cardW, height-marginY/2);
    //text(cards[p1Hand[i]].id, marginX/2+cardW/2+i*cardW, height-marginY/2);
  }
  //displaying hand cards for player 2 that have been assigned/dealt
  for (int i = 0; i<13; i++) {
    j = p2Hand[i];
    //update position in the class attribute for those cards
    cards[j].posX = marginX/2+cardW/2+i*cardW;
    cards[j].posY = marginY/2;
    cards[j].visible = true;
    //image(cards[j].cImg, cards[j].posX, cards[j].posY, cardW, cardW/691*1056);
  }
}

 

Leave a Reply