-
Hello,
Before I begin, I just wanna thank whoever took his time to read this code carefully and understood how it works.
So I have this tetris clone where everything works except that I couldn't find a way to implement a function that removes a full line AND shifts the shapes above down, I have tried changing the color to Black but that did not work. I would love if I could get some help with this as am really struggling with it.
#include <SFML/Graphics.hpp>
#include <cmath>
#include <cstdlib>
#include <iostream>
using namespace std;
//
// Tetris
//
class Shape
{
public:
sf::Color tiles[4][4];
sf::Vector2i pos;
// times since last downward movement
float time;
Shape();
// reinitialise the shape: move to top and change shape
void init();
// move downwards once per second
void update(float dt);
// render the shape
void draw(sf::RenderWindow& w);
// rotate the shape
void rotateLeft();
void rotateRight();
};
void Shape::rotateLeft()
{
sf::Color tmp[4][4];
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
tmp[i][j]=tiles[j][3-i];
}
}
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
tiles[i][j]=tmp[i][j];
}
}
}
void Shape::rotateRight()
{
rotateLeft();
rotateLeft();
rotateLeft();
}
Shape::Shape()
{
init();
}
void Shape::init()
{
// move to top and reset timer
pos.y = 0;
pos.x = 4;
time = 0.0f;
// fill with black tiles
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
tiles[i][j] = sf::Color::Black;
}
}
// two hardcoded shapes
switch(rand() % 2) {
case 0:
tiles[2][0] = sf::Color::Red;
tiles[2][1] = sf::Color::Red;
tiles[2][2] = sf::Color::Red;
tiles[2][3] = sf::Color::Red;
break;
case 1:
tiles[0][2] = sf::Color::Blue;
tiles[1][2] = sf::Color::Blue;
tiles[1][1] = sf::Color::Blue;
tiles[2][1] = sf::Color::Blue;
break;
}
}
void Shape::update(float dt)
{
time += dt;
if(time > 1) {
time = 0;
pos.y += 1;
}
}
void Shape::draw(sf::RenderWindow& w)
{
sf::CircleShape s;
s.setRadius(8);
s.setOrigin(8,8);
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
if(tiles[i][j] != sf::Color::Black) {
s.setFillColor(tiles[i][j]);
s.setPosition(sf::Vector2f(pos.x * 16 + 16 * i + 100, pos.y * 16 + 16 * j + 100));
w.draw(s);
}
}
}
}
class Board
{
public:
sf::Color tiles[12][20];
Board();
// add a shape to the board
void add(Shape& shape);
// check if a shape intersects with the board
bool intersect(Shape& shape);
// ******************* REMOVE FULL LINES ******************
void reduce();
// render board
void draw(sf::RenderWindow& w);
};
void Board::reduce()
{
}
bool Board::intersect(Shape& shape)
{
bool intersect = false;
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
if(tiles[i+shape.pos.x][j+shape.pos.y] != sf::Color::Black &&
shape.tiles[i][j] != sf::Color::Black)
intersect = true;
}
}
return intersect;
}
void Board::draw(sf::RenderWindow& w)
{
sf::CircleShape s;
s.setRadius(8);
s.setOrigin(8,8);
for(int i = 0; i < 12; i++) {
for(int j = 0; j < 20; j++) {
s.setFillColor(tiles[i][j]);
s.setPosition(sf::Vector2f(16 * i + 100, 16*j + 100));
w.draw(s);
}
}
}
void Board::add(Shape& shape)
{
for(int i = 0; i < 4; i++) {
for(int j = 0; j < 4; j++) {
if(shape.tiles[i][j] != sf::Color::Black) {
tiles[i + shape.pos.x][j + shape.pos.y] = shape.tiles[i][j];
}
}
}
}
Board::Board()
{
// fill with black
for(int i = 0; i < 20; i++) {
for(int j = 0; j < 12; j++) {
tiles[j][i] = sf::Color::Black;
}
}
// boundary
for(int i = 0; i < 12; i++) {
tiles[i][19] = sf::Color::Red;
}
for(int i = 0; i < 19; i++) {
tiles[0][i] = sf::Color::Red;
tiles[11][i] = sf::Color::Red;
}
}
int main()
{
sf::RenderWindow window(sf::VideoMode(512, 512), "Tetris");
sf::Clock clock;
clock.restart();
Shape shape;
Board board;
// run the program as long as the window is open
while (window.isOpen())
{
// check all the window's events that were triggered since the last iteration of the loop
sf::Event event;
while (window.pollEvent(event))
{
// "close requested" event: we close the window
if (event.type == sf::Event::Closed)
window.close();
if (event.type == sf::Event::KeyPressed) {
if(event.key.code == sf::Keyboard::Left) {
shape.pos.x -= 1;
if (board.intersect(shape)) {
shape.pos.x += 1;
cout << "intersect left" << endl;
}
}
if(event.key.code == sf::Keyboard::Right) {
shape.pos.x += 1;
if(board.intersect(shape)) {
shape.pos.x -= 1;
cout << "intersect right" << endl;
}
}
if(event.key.code == sf::Keyboard::Down) {
shape.pos.y += 1;
if(board.intersect(shape)) {
shape.pos.y -= 1;
cout << "intersect down" << endl;
}
}
if(event.key.code == sf::Keyboard::Up) {
shape.rotateLeft();
if(board.intersect(shape)) {
shape.rotateRight();
cout << "intersect rotate" << endl;
}
}
}
}
float dt = clock.restart().asSeconds();
shape.update(dt);
if(board.intersect(shape)) {
shape.pos.y -= 1;
board.add(shape);
board.reduce();
shape.init();
if(board.intersect(shape)) {
cout << "GAME OVER" << endl;
}
}
window.clear(sf::Color::Black);
board.draw(window);
shape.draw(window);
window.display();
}
return 0;
}
-
With your current setup, if I understood the code right, you'd simply have to iterate over your board color matrix, check if there's a line that's full and then move all the above lines one line down.
So with what exactly are you stuck?
-
With your current setup, if I understood the code right, you'd simply have to iterate over your board color matrix, check if there's a line that's full and then move all the above lines one line down.
So with what exactly are you stuck?
Thank you for your reply,
Yes you did understand the code right, I just couldn't find a way to make it check if a line is full.
-
You have a matrix so you have x and y. To check if a line is full you simply iterate for each line (for each y) all the elements (all the x) and if all of them are colored, the line is full.
-
You have a matrix so you have x and y. To check if a line is full you simply iterate for each line (for each y) all the elements (all the x) and if all of them are colored, the line is full.
This is what I reached so far (it doesn't work of course):
void Board::reduce(Board& board)
{
bool fullLine = false;
for (int i = 0; i < 20; i++) //for every y
{
for (int j = 0; j < 12; j++) //for every x in y
{
if (board.tiles[j][i] != sf::Color::Black )
{
cout << "Line is full";
}
}
}
}
It's checking for the board tiles in every x in y...
Any comments?
-
Something like this :)
void Board::reduce(Board& board)
{
bool fullLine = false;
for (int i = 0; i < 20; i++) //for every y
{
bool line_not_full = true;
for (int j = 0; j < 12; j++) //for every x in y
{
if (board.tiles[j][i] == sf::Color::Black )
{
line_not_full = false;
break;
}
}
if(!line_not_full)
{
// ...
}
}
}
-
I added a
cout <<"line is full";
to see when the program thinks the line is full, so I compiled it and this is what I get (Attached)
Apparently my argument in if (board.tiles[j][i] != sf::Color::Black );
is checking whenever a new tile spawns and couts line is full multiple times. This is really driving me crazy, any tips?