Matching Card Game - Memory Game
in
Share your Work
•
7 months ago
I month ago I started a thread asking for help with a game geared at helping disabled people gain coordination using a device a designed.
Chrisir jumped in and wrote a great game that I later just dressed up a little.
I thought I'd post the link here since its pretty fun to use and can be adapted to any theme by photo shopping the cards.
here are some screenshots and the link to all of the files.
I did it NFL theme because the patient is a huge NFL fan (NFL please don't sue me :) )
Thanks again Chrisir!!!
FILES:
import ddf.minim.*;
Minim minim;
AudioPlayer player;
AudioPlayer player1;
AudioPlayer player2;
//
// to do (optional): if uneven, place non-existent tile into right lower corner? With 8 tiles.
// have each level twice? Twice two cards, twice 4 cards etc.
//
// global vars and constants
//
// how many images on the hard drive? (this+1) Must be in data folder and named 0.jpg, 1.jpg etc.
final int howManyImages = 18;
//
//
// level stuff
final int maxLevel = 10; // how many levels, e.g. 6
int level = 0; // current level: 0
//
// state of program / game
final int stateGameStartUp = 0;
final int stateGamePlay = 1;
final int stateGameGoToNextLevelPhase1 = 2; // these two provide a small delay
final int stateGameGoToNextLevelPhase2 = 3;
final int stateGameOver = 4;
final int stateGameShowHelp = 5;
//
int state = stateGameStartUp; // current
//
//
// data for tiles
int winningNumberOfTiles;
int TILES_HORZ ;
int TILES_VERT ;
int TOTAL_TILES ;
int tileWidth;
int tileHeight;
Tile[] tiles ;
int imageCode;
//
// allRandomNumberImage is a check list to avoid using the same image twice as long as possible...
String allRandomNumberImage="#";
PImage[] images = new PImage [howManyImages];
//
// image entering stuff
// The var "openImages" is a counter that controls the input logic while making a move (aka opening two tiles).
// It says how many tiles are currently shown? 0, 1 or 2. (before the move, during the move and after the move).
// Only when openImages is == 2 we need to compare the two images (and reset openImages to 0 then).
int openImages = 0; // counter for current move
int lastImage = -1; // which was the last tile? (1st tile opened)
int currentImage = -1; // which is the current tile / image? (2nd tile opened)
int timer;
PImage imgLogo;
PImage imgWin;
PImage imgBackGlobal;
PImage imgBack = new PImage();// image of back of card
//
//
// ------------------------------------------------------------------
// two main funcs
//
void setup()
{
size( 1800, 900);
println("start of setup() ");
//
minim = new Minim(this);
player = minim.loadFile("nfl.wav");
player1 = minim.loadFile("mixdown.wav");
player2 = minim.loadFile("crowd.wav");
imgLogo = loadImage("logo.jpg");
imgBackGlobal = loadImage("cardBack.jpg");
imgBack = loadImage("cardBack.jpg");
imgWin = loadImage("youWon.jpg");
//
for (int x=0; x<howManyImages; x++) {
images[x]=loadImage(str(x)+".jpg");
} // for
//
timer=millis();
println("end of setup()");
}
//
void draw()
{
switch (state) {
case stateGameStartUp:
// start screen and new level
println("STARTUP");
startScreen() ;- break;
case stateGamePlay:
// play
//println("gameplay");
gamePlay();
break;
case stateGameGoToNextLevelPhase1:
// go to next level but only after a while
println("GTNL1");
goToNextLevel_1();
break;
case stateGameGoToNextLevelPhase2:
// go to next level but only after a while
println("GTNL2");
goToNextLevel_2();
break;
case stateGameOver:
// All levels done
println("gameover");
showWinScreen();
break;
case stateGameShowHelp:
// help - to do
println ( "This is your help / reached by F1 / not done yet...");
break;
default:
// error; shouldn't get here
println ("Error 86: unknown state");
break;
} // switch
//
} // func
// ---------------------------------------------------------------
// all funcs that get called from draw()
//
void startScreen() {
player.play();
player2.play();
// start screen : to do: make a cool start screen image
background(0);
// fill(2, 2, 255);
image(imgLogo, width/4.3, height/3.3);
textSize(80);
fill(255, 255, 255);
text ( " MEMORY GAME", width/3.5+200, height/2.2);
textSize(100);
text ( "LEVEL: "+(level+1), width/2.2, height/1.7);
text ( " ", 140, 301);
text ( " ", 140, 401);
// go next state but only after a while
if (timer + 5200 < millis()) {
initNewLevel();
state = stateGamePlay;
} // if
} // func
//
void gamePlay() {
// play
background(0);
drawTiles();
checkTwoShownTiles ();
} // func
//
void goToNextLevel_1() {
// wait before go to next level but only after a while
background(0);
drawTiles();
if (timer + 1000 < millis()) {
state=stateGameGoToNextLevelPhase2;
timer=millis();
} // if
} // func
//
void goToNextLevel_2() {
// go to next level but only after a while
background(0);
drawTiles();
showHooray();
if (timer + 1000 < millis()) {
// Was this the last level?
if (level==maxLevel) {
// yes, last level, game over.
state = stateGameOver; // won all levels
println("game over (all levels done).");
}
else
{
// no, go to next level.
state = stateGameStartUp;
timer=millis();
}
} // if
} // func
//
void showWinScreen() {
background(0);
drawTiles();
showHooray() ;
// show all levels done
background(0) ;
image(imgWin, 750,100);
fill(0); // black
noStroke();
rect (14, 104, 295, 100);
fill(255, 255, 255); // red
text ( " CONGRADULATIONS YOU FINISHED THE GAME!!!", 40, 160);
textSize(20);
text ( " CLICK MOUSE TO PLAY AGAIN", 40, 190);
player1.play();
} // func
//
// ------------------
//
void showHooray() { - fill(255);
strokeWeight(3);
stroke(0);
rect (4, 4, 325, 100);
fill(2, 2, 255);
text ( "LEVEL FINISHED!!!", 10, 60);
} // func
// -----------------------------------------------------------
// all funcs for inits
//
void initNewLevel () {
// - println("Init New Level #"+level);
//
// depending on current level set some basic vars
switch (level) {
case 0:
// just 2 pieces
TILES_HORZ =2;
TILES_VERT =1;
winningNumberOfTiles = 2;
break;
case 1:
// 4 pieces
TILES_HORZ =2;
TILES_VERT =2;
winningNumberOfTiles = 4;
break;
case 2:
TILES_HORZ =3;
TILES_VERT =2;
winningNumberOfTiles = 6;
break;
case 3:
TILES_HORZ =3;
TILES_VERT =3;
winningNumberOfTiles = 9; // is 8
break;
case 4:
TILES_HORZ =4;
TILES_VERT =3;
winningNumberOfTiles = 12; // is 12
break;
case 5:
TILES_HORZ =4;
TILES_VERT =4;
winningNumberOfTiles = 16; // is 16
break;
case 6:
TILES_HORZ =5;
TILES_VERT =4;
winningNumberOfTiles = 20; // is 20
break;
case 7:
TILES_HORZ =5;
TILES_VERT =5;
winningNumberOfTiles = 25; // is 24
break;
case 8:
TILES_HORZ =6;
TILES_VERT =5;
winningNumberOfTiles = 30; // is 30
break;
case 9:
TILES_HORZ =6;
TILES_VERT =6;
winningNumberOfTiles = 36; // is 36
break; -
default:
println("Error 55: Unknown Level");
break;
} // switch
//
// have other vars based on that defined
if (level>0) {
// level > 0
defineNormalTileSet() ;
}
else
{
// level is 0
defineTileSetForLevelZero() ;
}
textSize(54); - } // func
//
void defineTileSetForLevelZero() {
// level is 0
//
println ("******************LEVEL ZERO********************************");
// make data for tiles and the tiles
tileWidth=width/2;
tileHeight=height/2;
TOTAL_TILES = 2;
tiles = new Tile [TOTAL_TILES+1];
tiles[0] = new Tile (0, height-tileHeight);
tiles[1] = new Tile (tileWidth, height-tileHeight);
println("ENDTILES");
//
//
// make duplicates ---------------------
tiles[0].hasDuplicate=true; // both tiles have a duplicate now
tiles[1].hasDuplicate=true;
tiles[0].theImageCode=44; // both tiles get the same imageCode
tiles[1].theImageCode=44; // saying: you two are belonging together
int myImageIndex = giveImageIndex(); // get an index
tiles[0].loadImageIntoTile ( myImageIndex ); // same image
tiles[1].loadImageIntoTile ( myImageIndex ); - imgBack.copy(imgBackGlobal,
0, 0, imgBackGlobal.width, imgBackGlobal.height,
0, 0, tileWidth, 0); - }
//
void defineNormalTileSet() {
//
int i=0;
tileWidth=width/TILES_HORZ-1 ;
tileHeight=tileWidth/2;
TOTAL_TILES = (TILES_HORZ)*(TILES_VERT);
// not even?
if (winningNumberOfTiles%2 != 0 ) { - winningNumberOfTiles--;
}
tiles = new Tile [TOTAL_TILES+1];
// now define the tiles without the images ------------------
for (int x=0; x<TILES_HORZ; x++) {
for (int y=0; y<TILES_VERT; y++) {
tiles[i] = new Tile (tileWidth*x, tileHeight*y );
i++;
}
}
//
// make duplicates ---------------------
i=0;
// this arraylist "intTileIndexes" serves as a memory which
// cards we already treated
ArrayList <Integer> intTileIndexes = new ArrayList();
intTileIndexes.clear();
// fill the arraylist
for (int x=0; x<TILES_HORZ; x++) {
for (int y=0; y<TILES_VERT; y++) {
intTileIndexes.add( i );
i++;
}
}
// still make duplicates ------
i=0;
imageCode=0;
allRandomNumberImage="#";
for (int x=0; x<TILES_HORZ; x++) {
for (int y=0; y<TILES_VERT; y++) {
makeItHaveAnDuplicate( intTileIndexes, i );
i++;
} // for
} // for
//
// if not even, make one tile inexistent
i=0;
for (int x=0; x<TILES_HORZ; x++) {
for (int y=0; y<TILES_VERT; y++) {
// if our tile doesn't have a duplicate yet
if (!tiles[i].hasDuplicate) {
// we don't show it
tiles[i].exists=false;
} // if
i++;
} // for
} // for
//
imgBack.copy(imgBackGlobal,
0, 0, imgBackGlobal.width, imgBackGlobal.height,
0, 0, tileWidth, 0);
} // func
//
void makeItHaveAnDuplicate( ArrayList <Integer> intTileIndexes, int i ) {
// makeItHaveAnDuplicate
// if our tile doesn't have a duplicate yet, then it's on our to do list:
if (!tiles[i].hasDuplicate) {
// we search a random partner
int safetyCounter = 0; // that avoids that the loop takes too long...
// now we calculate a random number which is a index for intTileIndexes
int j2 = int(random (0, intTileIndexes.size()));
int j = intTileIndexes.get(j2);
// remark: he can't find a partner when the tiles are e.g. 9 (uneven),
// then we get an error here but that's ok
while ( (i==j || tiles[j].hasDuplicate ) &&
(safetyCounter<50) ) {
j2 = int(random (0, intTileIndexes.size()));
j = intTileIndexes.get(j2);
safetyCounter++;
}
// Did he found one?
if ( (i!=j) && (!tiles[j].hasDuplicate) ) {
// yes
// remove those from the arraylist
removeFromArrayList (intTileIndexes, i);
removeFromArrayList (intTileIndexes, j);
tiles[i].hasDuplicate=true; // both tiles have a duplicate now
tiles[j].hasDuplicate=true;
tiles[i].theImageCode=imageCode; // both tiles get the same imageCode
tiles[j].theImageCode=imageCode; // saying: you two are belonging together
int myImageIndex=giveImageIndex(); // get a name
tiles[i].loadImageIntoTile ( myImageIndex ); // same image
tiles[j].loadImageIntoTile ( myImageIndex );
//
imageCode++; // increase the code since it must be unique
}
else {
// println ("error 119");
} // else
} // if
}
//
int giveImageIndex() {
// gives a image file name (that is new)
int randomNumberImage = int (random(0, howManyImages));
int safetyCounter = 0;
// while not a new image keep on searching
while ( (allRandomNumberImage.indexOf ( "#"+ str ( randomNumberImage ) + "#" ) > -1 ) &&
(safetyCounter<30) ) {
randomNumberImage = int (random(0, howManyImages));
safetyCounter++;
}
// found a new one
allRandomNumberImage = allRandomNumberImage + "#" + str( randomNumberImage ) + "#" ;
// return value
// return str( randomNumberImage ) +".jpg";
return randomNumberImage;
} // func
//
void removeFromArrayList (ArrayList<Integer> myArrayList, int iToBeRemoved) {
// removes one item iToBeRemoved.
// With an array, we say balls.length. With an ArrayList,
// we say balls.size(). The length of an ArrayList is dynamic.
// Notice how we are looping through the ArrayList backwards.
// This is because we are deleting elements from the list.
for (int i = myArrayList.size()-1; i >= 0; i--) {
int iFromArrayList = int ( myArrayList.get(i));
if ( iFromArrayList==iToBeRemoved ) {
myArrayList.remove(i);
return;
}
} // for
} // func
//
// Inputs -----------------------------------------------------------------
//
void mousePressed() {
// switch state
switch (state) {
case stateGamePlay:
if (openImages != 2) {
// search
int x=-1;
for (int x2=0; x2<TOTAL_TILES; x2++) {
if (tiles[x2].mouseInside() && tiles[x2].exists
&& !tiles[x2].isSolved && !tiles[x2].isShown ) {
x=x2;
break;
} // if
} // for
//
// found?
if (x>-1) {
// when one image is open make sure that the 2nd is a different one
if ( ((openImages == 1) && ( x != lastImage)) ||
(openImages == 0) ) {
// yes?
if (tiles[x].mouseInside()) {
tiles[x].isShown=true;
openImages++;
if (openImages == 1) {
lastImage=x;
}
else
{
currentImage=x;
timer=millis();
} // else
} // if
} // if
} // if
} // if
break;
case stateGameOver:
level=0;
println("Blammo");
// initNewLevel();
// state = stateGamePlay;
state = stateGameStartUp;
timer=millis();
break;
default:
// do nothing
break;
} // switch
//
} // func
void keyPressed () {
if (state==stateGameOver) {
level=0;
openImages = 0; // counter for current move
lastImage = -1; // which was the last tile? (1st tile opened)
currentImage = -1; // which is the current tile / image? (2nd tile opened)
state = stateGameStartUp;
timer=millis();
}
}
// -------------------------------------------------------
// smaller tools
//
//
void drawTiles() {
// draw it
for (int x=0; x<TOTAL_TILES; x++) {//println("X: "+x+" TOTTILES: "+TOTAL_TILES);
tiles[x].draw();
}
//
}
//
void checkTwoShownTiles () {
// after a move:
// if two images are shown we have to check them
if (openImages == 2) {
// but only after a while
if (timer + 1000 < millis()) {
// two are shown (the user has made his move)
openImages=0; // reset
// check
if ( tiles[currentImage].theImageCode == tiles[lastImage].theImageCode)
{
// correct
println ("Good");
tiles[currentImage].isSolved=true;
tiles[lastImage].isSolved=true;
checkIfSolved();
}
else
{
// not correct: the are hidden again
tiles[currentImage].isShown=false;
tiles[lastImage].isShown=false;
}
}
}
} // func
//
void checkIfSolved() {
// check if this level is complete
int counter = 0;
// Are all shown and solved?
for (int x2=0; x2<TOTAL_TILES; x2++) {
if (tiles[x2].isSolved) {
counter ++;
}
}
//
if (counter==winningNumberOfTiles)
{
state=stateGameGoToNextLevelPhase1; // won this level -> stateGameGoToNextLevelPhase1
println ("You won this level.");
level++;
timer=millis();
}
}
//
// ================================================================
//Tile Class (one image card)
class Tile
{
int imgIndex; // the image index
float px; // position
float py;
// those three (4) could be unified to a var stateOfTile : nonExistent, withoutDuplicate, covered, shown, solved
boolean isShown = false; // shown or covered?
boolean isSolved = false; // the duplicate is not only shown but correct and now permanently shown
boolean exists = true; // does the tile exist at all?
boolean hasDuplicate = false; // while init not all images have a duplicate yet
//
int theImageCode = -1; // the partners are identified with the same number (which is otherwise meaningless, except both have the same).
//
// constr
Tile ( float px_, float py_ )
{
px=px_;
py=py_;
} // constr
//
void loadImageIntoTile ( int imageName_ ) {
imgIndex = imageName_;
} // method
//
void draw()
{
// if it exists at all?
if (exists) {
// we show it.
// uncovered or covered?
if (isShown) {
// open
if (images[imgIndex]!=null){
image(images[imgIndex], px, py,tileWidth,tileHeight);}
else
println ("images[imgIndex] is null");
stroke(255);
noFill();
rect(px, py, tileWidth, tileHeight);
// if tile is solved correctly
if (isSolved)
{
// we show the OK sign
textSize(34);
fill(255);
strokeWeight(4);
stroke(0);
rect(px+tileWidth-170, py+tileHeight-58, 160, 40);
fill(2, 2, 255);
text( "MATCH!", px+tileWidth-150, py+tileHeight-27 );
fill(255);
}
}
else
{println("Tiles not shown");
// not shown / covered tile with a "?" or imgBack
image(imgBack, px, py,tileWidth,tileHeight);
textSize(54);
stroke(255);
noFill();
//fill(0);
rect(px, py, tileWidth, tileHeight); - } // else
} // if (exists) {
} // method
//
boolean mouseInside()
{
return ((px <= mouseX && mouseX <= px + tileWidth) &&
(py <= mouseY && mouseY <= py + tileHeight));
} // method
//
} // class