main.cpp
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include "world.h"
int main()
{
initializegame();
generatecurrentlevel(levelnumber);
while(window.isOpen() && !sf::Keyboard::isKeyPressed(sf::Keyboard::Escape))
{
while(window.pollEvent(event))
if(event.type == sf::Event::Closed) window.close();
think();
drawlevel();
window.display();
}
return 0;
}
world.h
#ifndef WORLD_H
#define WORLD_H
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#define CARDDOWN 0
#define CARDUP 1
#define FLAG 2
#define BOMB 3
extern sf::RenderWindow window;
extern sf::Event event;
extern sf::Font font;
extern sf::Texture itemstexture;
extern sf::Sprite itemssprite[10];
extern sf::SoundBuffer soundbuffer_cardflip, soundbuffer_explosion, soundbuffer_win;
extern sf::Sound sound_cardflip, sound_explosion, sound_win;
struct cardsshowing
{
bool showing;
int x, y;
};
extern cardsshowing cardshown[2];
extern int numberofcardsshowing;
extern sf::Clock myclock; // Used for pausing for a second while two cards are shown
struct levelstruct
{
bool bomb;
// Bomb has to be either flagged or otherwise revealed to count
bool flag;
bool bombrevealed;
int surroundingbombsamount;
int cardindex;
bool cardfaceshowing;
bool matched;
};
extern levelstruct level[100][100];
extern int lives;
extern int levelnumber;
extern int flipsremaining;
extern int setsof;
extern int numberofbombs;
extern int levelwidth, levelheight;
extern int xoffset;
extern int yoffset;
extern int xspacing;
extern int yspacing;
void think();
void initializegame();
void generatelevel(int width, int height, int numberofbombss, int setspertype, int nofflips);
void generatecurrentlevel(int which);
void drawlevel();
int countbombsat(int x, int y);
bool checkforwin();
#endif
world.cpp
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include "windows.h"
#include "world.h"
#include <iostream> // Used to convert float to string for draw(text)
#include <sstream> // Used to convert float to string for draw(text)
#include <vector>
sf::RenderWindow window;
sf::Event event;
sf::Font font;
sf::Texture itemstexture;
sf::Sprite itemssprite[10];
sf::Clock myclock;
sf::SoundBuffer soundbuffer_cardflip, soundbuffer_explosion, soundbuffer_win;
sf::Sound sound_cardflip, sound_explosion, sound_win;
int lives;
int levelnumber;
int flipsremaining;
int setsof;
int numberofbombs;
cardsshowing cardshown[2];
int numberofcardsshowing;
levelstruct level[100][100];
int levelwidth, levelheight;
int xoffset;
int yoffset;
int xspacing;
int yspacing;
bool leftmousedown, leftmousedownlastframe;
bool rightmousedown, rightmousedownlastframe;
void think()
{
sf::Vector2i mousepos = sf::Mouse::getPosition(window);
int cardx, cardy;
if(sf::Mouse::isButtonPressed(sf::Mouse::Left)) leftmousedown=true;
else leftmousedown=false;
if(sf::Mouse::isButtonPressed(sf::Mouse::Right)) rightmousedown=true;
else rightmousedown=false;
bool overcard=false;
if(mousepos.x>xoffset && mousepos.x<xspacing*(levelwidth-1)+64+xoffset)
if(mousepos.y>yoffset && mousepos.y<yspacing*(levelheight-1)+94+yoffset)
if((mousepos.x-xoffset)%xspacing<64 && (mousepos.y-yoffset)%yspacing<94)
{
overcard=true;
cardx=(mousepos.x-xoffset)/xspacing;
cardy=(mousepos.y-yoffset)/yspacing;
}
// Set or clear flag
if(!rightmousedown && rightmousedownlastframe && overcard)
{
level[cardy][cardx].flag=!level[cardy][cardx].flag;
}
// Clicked on a bomb OR ran out of flips
if(!leftmousedown && leftmousedownlastframe && overcard && level[cardy][cardx].bomb || flipsremaining<1)
{
lives--;
if(lives<1)
{
levelnumber=1;
lives=5;
}
generatecurrentlevel(levelnumber);
sound_explosion.play();
}
// Flip card
// MUST use "else if" here or above code will reset board and this code will cause a click
else if(!leftmousedown && leftmousedownlastframe && overcard && numberofcardsshowing<2 && !level[cardy][cardx].matched && !level[cardy][cardx].bomb)
{
level[cardy][cardx].cardfaceshowing=!level[cardy][cardx].cardfaceshowing;
if(level[cardy][cardx].cardfaceshowing)
{
cardshown[numberofcardsshowing].x=cardx;
cardshown[numberofcardsshowing].y=cardy;
cardshown[numberofcardsshowing].showing=true;
numberofcardsshowing++; // We turned a card face up
if(numberofcardsshowing>1) // We have two cards up. Start the clock for a second until they both flip back automatically
{
myclock.restart();
}
flipsremaining--;
}
else numberofcardsshowing--; // We turned a card back face down
sound_cardflip.play();
}
if(checkforwin())
{
levelnumber++;
generatecurrentlevel(levelnumber);
sound_win.play();
}
// Check for match
if(numberofcardsshowing>1 && level[cardshown[0].y][cardshown[0].x].cardindex==level[cardshown[1].y][cardshown[1].x].cardindex)
{
// MATCH!!
// Uncover all cards with the same number
int matchnumber=level[cardshown[0].y][cardshown[0].x].cardindex;
for(int j=0; j<levelheight; j++)
{
for(int i=0; i<levelwidth; i++)
{
if(level[j][i].cardindex==matchnumber) level[j][i].matched=true;
}
}
}
// Flip cards back if enough time passed
sf::Time elapsed = myclock.getElapsedTime();
if(numberofcardsshowing>1 && elapsed.asMilliseconds()>400)
{
level[cardshown[0].y][cardshown[0].x].cardfaceshowing=false;
level[cardshown[1].y][cardshown[1].x].cardfaceshowing=false;
cardshown[0].showing=false;
cardshown[1].showing=false;
numberofcardsshowing=0;
}
leftmousedownlastframe=leftmousedown;
rightmousedownlastframe=rightmousedown;
}
void drawlevel()
{
std::stringstream buff;
int i, j;
window.clear();
sf::Text text;
text.setFont(font);
text.setCharacterSize(36);
for(j=0; j<levelheight; j++)
{
for(i=0; i<levelwidth; i++)
{
if(level[j][i].cardfaceshowing && !level[j][i].matched)
{
itemssprite[CARDUP].setPosition(i*xspacing+xoffset, j*yspacing+yoffset);
window.draw(itemssprite[CARDUP]);
}
else if(!level[j][i].matched)
{
itemssprite[CARDDOWN].setPosition(i*xspacing+xoffset, j*yspacing+yoffset);
window.draw(itemssprite[CARDDOWN]);
}
if(level[j][i].bomb)
{
itemssprite[BOMB].setPosition(i*xspacing+xoffset, j*yspacing+yoffset);
// window.draw(itemssprite[BOMB]);
}
if(level[j][i].flag)
{
itemssprite[FLAG].setPosition(i*xspacing+xoffset, j*yspacing+yoffset);
window.draw(itemssprite[FLAG]);
}
// All this just to show card number on screen
text.setFillColor(sf::Color::Black);
buff.str("");
buff<<level[j][i].cardindex; // Card matching number
text.setString(buff.str());
if(level[j][i].cardindex<10) text.setPosition(i*xspacing+xoffset+26,j*yspacing+yoffset+22);
else text.setPosition(i*xspacing+xoffset+18,j*yspacing+yoffset+22);
// Show card number
if(level[j][i].cardfaceshowing && !level[j][i].matched) window.draw(text);
// Show surrounding bomb count
if(level[j][i].matched && !level[j][i].bomb)
{
buff.str("");
buff<<level[j][i].surroundingbombsamount; // Number of bombs around
text.setString(buff.str());
text.setFillColor(sf::Color::White);
if(level[j][i].surroundingbombsamount<10) text.setPosition(i*xspacing+xoffset+26,j*yspacing+yoffset+22);
else text.setPosition(i*xspacing+xoffset+18,j*yspacing+yoffset+22);
window.draw(text);
}
}
}
text.setFillColor(sf::Color::White);
buff.str("");
buff<<"Level: "<<levelnumber;
text.setString(buff.str());
text.setPosition(1000,0);
window.draw(text);
text.setFillColor(sf::Color::White);
buff.str("");
buff<<"Lives: "<<lives;
text.setString(buff.str());
text.setPosition(1000,50);
window.draw(text);
text.setFillColor(sf::Color::White);
buff.str("");
buff<<"Flips: "<<flipsremaining;
text.setString(buff.str());
text.setPosition(1000,100);
window.draw(text);
text.setFillColor(sf::Color::White);
buff.str("");
buff<<"Bombs: "<<numberofbombs;
text.setString(buff.str());
text.setPosition(1000,150);
window.draw(text);
text.setFillColor(sf::Color::White);
buff.str("");
buff<<"Sets of: "<<setsof;
text.setString(buff.str());
text.setPosition(1000,200);
window.draw(text);
}
int countbombsat(int x, int y)
{
int bombcount=0;
if(x>0) bombcount+=level[y][x-1].bomb;
if(x<levelwidth-1) bombcount+=level[y][x+1].bomb;
if(y>0) bombcount+=level[y-1][x].bomb;
if(y<levelheight-1) bombcount+=level[y+1][x].bomb;
if(x>0 && y>0) bombcount+=level[y-1][x-1].bomb;
if(x<levelwidth-1 && y>0) bombcount+=level[y-1][x+1].bomb;
if(x<levelwidth-1 && y<levelheight-1) bombcount+=level[y+1][x+1].bomb;
if(x>0 && y<levelheight-1) bombcount+=level[y+1][x-1].bomb;
return bombcount;
}
void generatecurrentlevel(int which)
{
if(which==1) generatelevel(4, 4, 1, 4, 30);
else if(which==2) generatelevel(5, 5, 2, 4, 40);
else if(which==3) generatelevel(7, 5, 3, 3, 50);
else if(which==4) generatelevel(7, 6, 3, 3, 60);
else if(which==5) generatelevel(7, 6, 4, 3, 70);
else if(which==6) generatelevel(8, 6, 4, 3, 80);
else if(which==7) generatelevel(6, 5, 5, 3, 70);
else generatelevel(6, 6, 6, 3, 60);
}
void generatelevel(int width, int height, int numberofbombss, int setspertype, int nofflips)
{
struct levelarrayindex { int x, y; };
std::vector <levelarrayindex> lai(width*height); // A list of all array coordinates to help populate level array with bombs, cards, etc.
// Check to make sure we aren't requesting too many bombs
if(numberofbombss>width*height/3)
{
MessageBoxA(NULL, "Too many bombs requested for level size", "ERROR", MB_ICONERROR | MB_OK);
exit(1);
}
levelwidth=width;
levelheight=height;
setsof=setspertype;
flipsremaining=nofflips;
numberofbombs=numberofbombss;
xoffset=10;
yoffset=10;
xspacing=100;
yspacing=120;
// Reset everything
for(int j=0; j<levelheight; j++)
for(int i=0; i<levelwidth; i++)
{
level[j][i].bomb=false;
level[j][i].bombrevealed=false;
level[j][i].cardfaceshowing=false;
level[j][i].cardindex=0;
level[j][i].flag=false;
level[j][i].matched=false;
level[j][i].surroundingbombsamount=0;
}
numberofcardsshowing=0;
cardshown[0].showing=false;
cardshown[1].showing=false;
// Set up vector coordinates
for(int j=0; j<height; j++)
{
for(int i=0; i<width; i++)
{
lai[j*width+i].x=i;
lai[j*width+i].y=j;
}
}
// Generate bombs
int bombcount=0;
while(bombcount<numberofbombss)
{
int bombindex = rand() % lai.size();
level[lai[bombindex].y][lai[bombindex].x].bomb=true;
// printf("%d, %d\n",lai[bombindex].x, lai[bombindex].y);
// printf("%d\n",level[lai[bombindex].y][lai[bombindex].x].bomb);
// printf("%d\n",lai.size());
lai.erase(lai.begin() + bombindex);
bombcount++;
}
int numbertouse=1;
int numbersremaining=setspertype; // # of each number for cards
while(lai.size()>0)
{
int numberindex = rand() % lai.size();
level[lai[numberindex].y][lai[numberindex].x].cardindex=numbertouse;
lai.erase(lai.begin() + numberindex);
numbersremaining--;
if(numbersremaining<1)
{
numbertouse++;
numbersremaining=4;
}
// If we only have 2 cards left, fill them up with the latest number
if(lai.size()<3)
{
while(lai.size()>0)
{
level[lai[0].y][lai[0].x].cardindex=numbertouse;
lai.erase(lai.begin());
}
}
}
for(int j=0; j<levelheight; j++)
for(int i=0; i<levelwidth; i++)
level[j][i].surroundingbombsamount=countbombsat(i, j);
}
bool checkforwin()
{
// Check here to make sure all bombs are either marked or revealed
int flippedcardscount=0;
for(int j=0; j<levelheight; j++)
for(int i=0; i<levelwidth; i++)
if(!level[j][i].bomb) flippedcardscount+=level[j][i].matched;
// printf("flipped cards: %d, number of bombs: %d, width*height: %d\n",flippedcardscount,numberofbombs,levelwidth*levelheight);
if(flippedcardscount+numberofbombs==levelwidth*levelheight) return true;
return false;
}
void initializegame()
{
srand((unsigned int)time(NULL));
window.create(sf::VideoMode(1240, 720), "MatchSweeper");
if(!font.loadFromFile("forced square.ttf"))
{
MessageBoxA(NULL, "Error loading font", "ERROR", MB_ICONERROR | MB_OK);
exit(1);
}
if(!itemstexture.loadFromFile("graphics/items.png"))
{
MessageBoxA(NULL, "Error loading items.png", "ERROR", MB_ICONERROR | MB_OK);
exit(1);
}
if(!soundbuffer_cardflip.loadFromFile("sounds/flipcard.wav"))
{
MessageBoxA(NULL, "Error loading flipcard.wav", "ERROR", MB_ICONERROR | MB_OK);
exit(1);
}
sound_cardflip.setBuffer(soundbuffer_cardflip);
if(!soundbuffer_explosion.loadFromFile("sounds/explosion.wav"))
{
MessageBoxA(NULL, "Error loading explosion.wav", "ERROR", MB_ICONERROR | MB_OK);
exit(1);
}
sound_explosion.setBuffer(soundbuffer_explosion);
if(!soundbuffer_win.loadFromFile("sounds/win.wav"))
{
MessageBoxA(NULL, "Error loading win.wav", "ERROR", MB_ICONERROR | MB_OK);
exit(1);
}
sound_win.setBuffer(soundbuffer_win);
itemssprite[0].setTexture(itemstexture);
itemssprite[0].setTextureRect(sf::IntRect(0, 0, 64, 128));
itemssprite[1].setTexture(itemstexture);
itemssprite[1].setTextureRect(sf::IntRect(64, 0, 64, 128));
itemssprite[2].setTexture(itemstexture);
itemssprite[2].setTextureRect(sf::IntRect(128, 0, 64, 128));
itemssprite[3].setTexture(itemstexture);
itemssprite[3].setTextureRect(sf::IntRect(196, 0, 64, 128));
lives=5;
levelnumber=1;
}