#define INPUT_KEYS    // Compile keys handler.
#define INPUT_GYRO    // Compile motion controller.
#undef OUTPUT_MAX     // Compile LED matrix as output.
#define OUTPUT_TFT    // Compile LCD display as output.
#define SOUND         // Compile sounds.

#ifdef INPUT_GYRO
#include <Wire.h>
#endif

#ifdef OUTPUT_MAX
#include <LedControl.h>
#endif

#ifdef OUTPUT_TFT
#include <TFT.h>
#include <SPI.h>
#endif

#ifdef OUTPUT_MAX
#define PIN_DATA          8
#define PIN_LOAD          9
#define PIN_CLOCK         10

LedControl matrix = LedControl(PIN_DATA, PIN_CLOCK, PIN_LOAD, 5);
#endif // OUTPUT_MAX

#ifdef OUTPUT_TFT
#define PIN_CS            10
#define PIN_DC            9
#define PIN_RESET         8

TFT tft = TFT(PIN_CS, PIN_DC, PIN_RESET);
#endif // OUTPUT_TFT

#define PIN_SWITCH        2

#ifdef INPUT_KEYS
#define PIN_BUTTON_LEFT   6
#define PIN_BUTTON_RIGHT  7
#define PIN_BUTTON_DOWN   5
#define PIN_BUTTON_UP     4
#endif // INPUT_KEYS

#ifdef INPUT_GYRO
#define MPU               0x68
#define GYRO_SENSITIVITY  2500
#endif // INPUT_GYRO

#ifdef OUTPUT_MAX
#define SCREEN_WIDTH      16
#define SCREEN_HEIGHT     16
#define PLAYGROUND_WIDTH  12
#define PLAYGROUND_HEIGHT 15
#define MATRIX_SIZE       8
#endif // OUTPUT_MAX

#ifdef OUTPUT_TFT
#define SCREEN_WIDTH_IN_PIXEL   128
#define SCREEN_HEIGHT_IN_PIXEL  160
#define SCREEN_WIDTH            16
#define SCREEN_HEIGHT           20
#define PLAYGROUND_WIDTH        12
#define PLAYGROUND_HEIGHT       19
#define PLAYGROUND_LEFT         (SCREEN_WIDTH - PLAYGROUND_WIDTH) / 2
#define PLAYGROUND_RIGHT        PLAYGROUND_LEFT + PLAYGROUND_WIDTH - 1
#define PLAYGROUND_BOTTOM       1
#define PLAYGROUND_TOP          PLAYGROUND_BOTTOM + PLAYGROUND_HEIGHT - 1
#define PIXEL_SIZE              8
#endif // OUTPUT_TFT

#ifdef SOUND
#define PIN_PIEZO         3
#endif

void setup()
{
#ifdef DEBUG
  Serial.begin(9600);
#endif

#ifdef INPUT_GYRO

  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);
  Wire.write(0);
  Wire.endTransmission(true);

#endif // INPUT_GYRO

  pinMode(PIN_SWITCH, INPUT);

#ifdef INPUT_KEYS

  pinMode(PIN_BUTTON_LEFT, INPUT);
  pinMode(PIN_BUTTON_RIGHT, INPUT);
  pinMode(PIN_BUTTON_DOWN, INPUT);
  pinMode(PIN_BUTTON_UP, INPUT);

#endif // INPUT_KEYS

#ifdef OUTPUT_MAX

  for (byte matrixDevice = 0; matrixDevice < matrix.getDeviceCount(); matrixDevice++)
  {
    matrix.shutdown(matrixDevice, false);
  }

#endif // OUTPUT_MAX

#ifdef OUTPUT_TFT

  tft.begin();
  tft.background(0, 0, 0);
  tft.stroke(0x08, 0x08, 0x08);

  for (byte grid = 0; grid <= PLAYGROUND_WIDTH; grid++)
  {
    byte coordinateX = ((SCREEN_WIDTH - PLAYGROUND_WIDTH) / 2) * PIXEL_SIZE + (grid * PIXEL_SIZE);
    tft.line(0, coordinateX, SCREEN_HEIGHT_IN_PIXEL, coordinateX);
  }

  for (byte grid = 0; grid <= PLAYGROUND_HEIGHT; grid++)
  {
    byte startX = ((SCREEN_WIDTH - PLAYGROUND_WIDTH) / 2) * PIXEL_SIZE;
    byte endX = (SCREEN_WIDTH - ((SCREEN_WIDTH - PLAYGROUND_WIDTH) / 2)) * PIXEL_SIZE;
    byte coordinateY = ((SCREEN_HEIGHT - PLAYGROUND_HEIGHT) / 2) * PIXEL_SIZE + (grid * PIXEL_SIZE);
    tft.line(SCREEN_HEIGHT_IN_PIXEL - coordinateY, startX, SCREEN_HEIGHT_IN_PIXEL - coordinateY, endX);
  }

#endif // OUTPUT_TFT

#ifdef SOUND
  pinMode(PIN_PIEZO, OUTPUT);
#endif

  startNewGame(false);
}

void loop()
{
  handleControls();

  if (moveFigureDown() == false)
    startNewGame(true);
}

#define NUMBER_OF_FIGURES 7
#define FIGURE_SIZE       4

const byte figures[NUMBER_OF_FIGURES][4][FIGURE_SIZE][2] =
{
  {
    { { -1,  0 }, {  0,  0 }, {  1,  0 }, {  2,  0 } },
    { {  0, -1 }, {  0,  0 }, {  0,  1 }, {  0,  2 } },
    { { -1,  0 }, {  0,  0 }, {  1,  0 }, {  2,  0 } },
    { {  0, -1 }, {  0,  0 }, {  0,  1 }, {  0,  2 } }
  },
  {
    { { -1, -1 }, { -1,  0 }, {  0,  0 }, {  1,  0 } },
    { { -1,  1 }, {  0,  1 }, {  0,  0 }, {  0, -1 } },
    { { -1,  0 }, {  0,  0 }, {  1,  0 }, {  1,  1 } },
    { {  0,  1 }, {  0,  0 }, {  0, -1 }, {  1, -1 } }
  },
  {
    { { -1,  0 }, {  0,  0 }, {  1,  0 }, {  1, -1 } },
    { { -1, -1 }, {  0, -1 }, {  0,  0 }, {  0,  1 } },
    { { -1,  1 }, { -1,  0 }, {  0,  0 }, {  1,  0 } },
    { {  0, -1 }, {  0,  0 }, {  0,  1 }, {  1,  1 } }
  },
  {
    { { -1,  0 }, {  0,  0 }, {  0, -1 }, {  1, -1 } },
    { { -1, -1 }, { -1,  0 }, {  0,  0 }, {  0,  1 } },
    { { -1,  0 }, {  0,  0 }, {  0, -1 }, {  1, -1 } },
    { { -1, -1 }, { -1,  0 }, {  0,  0 }, {  0,  1 } }
  },
  {
    { { -1, -1 }, {  0, -1 }, {  0,  0 }, {  1,  0 } },
    { {  0,  1 }, {  0,  0 }, {  1,  0 }, {  1, -1 } },
    { { -1, -1 }, {  0, -1 }, {  0,  0 }, {  1,  0 } },
    { {  0,  1 }, {  0,  0 }, {  1,  0 }, {  1, -1 } }
  },
  {
    { { -1,  0 }, {  0,  0 }, {  1,  0 }, {  0, -1 } },
    { {  0,  1 }, {  0,  0 }, {  0, -1 }, { -1,  0 } },
    { { -1,  0 }, {  0,  0 }, {  1,  0 }, {  0,  1 } },
    { {  0,  1 }, {  0,  0 }, {  0, -1 }, {  1,  0 } }
  },
  {
    { {  0,  0 }, {  1,  0 }, {  1, -1 }, {  0, -1 } },
    { {  0,  0 }, {  1,  0 }, {  1, -1 }, {  0, -1 } },
    { {  0,  0 }, {  1,  0 }, {  1, -1 }, {  0, -1 } },
    { {  0,  0 }, {  1,  0 }, {  1, -1 }, {  0, -1 } }
  }
};

byte screen[SCREEN_WIDTH][SCREEN_HEIGHT];

struct Figure
{
  byte x;
  byte y;
  byte type;
  byte color;
  byte rotation;
  bool matrix[FIGURE_SIZE][FIGURE_SIZE];
};

struct Figure figure;

bool figureIsDropingDown;

#ifdef INPUT_GYRO

struct Vector
{
  int16_t x, y, z;
};

struct Vector accelerometerInit;
struct Vector gyroscopeInit;

#endif // INPUT_GYRO

void handleControls()
{
  if (digitalRead(PIN_SWITCH) == LOW)
  {

#ifdef INPUT_KEYS

    if (figureIsDropingDown == false)
    {
      bool pressedLeft = false;
      bool pressedRight = false;
      bool pressedDown = false;
      bool pressedUp = false;

      for (unsigned int iteration = 0; iteration < 15000; iteration++)
      {
        if (digitalRead(PIN_BUTTON_LEFT) == HIGH)
        {
          pressedLeft = true;
        }
        if (digitalRead(PIN_BUTTON_RIGHT) == HIGH)
        {
          pressedRight = true;
        }
        if (digitalRead(PIN_BUTTON_DOWN) == HIGH)
        {
          pressedDown = true;
        }
        if (digitalRead(PIN_BUTTON_UP) == HIGH)
        {
          pressedUp = true;
        }
      }

      if (pressedLeft == true)
        moveFigureLeft();

      if (pressedRight == true)
        moveFigureRight();

      if (pressedDown == true)
        dropFigure();

      if (pressedUp == true)
        turnFigure();
    }
    else
    {
      delay(50);
    }

#endif // INPUT_KEYS

  }
  else
  {

#ifdef INPUT_GYRO

    if (figureIsDropingDown == false)
    {
      struct Vector accelerometer;
      struct Vector gyroscope;

      Wire.beginTransmission(MPU);
      Wire.write(0x3B);
      Wire.endTransmission(false);
      Wire.requestFrom(MPU, 12, true);

      accelerometer.x = Wire.read() << 8 | Wire.read();
      accelerometer.y = Wire.read() << 8 | Wire.read();
      accelerometer.z = Wire.read() << 8 | Wire.read();

      gyroscope.x = Wire.read() << 8 | Wire.read();
      gyroscope.y = Wire.read() << 8 | Wire.read();
      gyroscope.z = Wire.read() << 8 | Wire.read();

#ifdef DEBUG
      Serial.print("Accelerometer (X/Y/Z): ");
      Serial.print(accelerometer.x);
      Serial.print(" / ");
      Serial.print(accelerometer.y);
      Serial.print(" / ");
      Serial.println(accelerometer.z);

      Serial.print("Gyroscope (X/Y/Z): ");
      Serial.print(gyroscope.x);
      Serial.print(" / ");
      Serial.print(gyroscope.y);
      Serial.print(" / ");
      Serial.println(gyroscope.z);
#endif // DEBUG

      if (accelerometerInit.x - accelerometer.x > +GYRO_SENSITIVITY)
      {
        moveFigureLeft();
      }
      else if (accelerometerInit.x - accelerometer.x < -GYRO_SENSITIVITY)
      {
        moveFigureRight();
      }
      else if (accelerometerInit.z - accelerometer.z < -GYRO_SENSITIVITY)
      {
        dropFigure();
      }
      else if (accelerometerInit.z - accelerometer.z > +GYRO_SENSITIVITY)
      {
        turnFigure();
      }

      delay(400);
    }
    else
    {
      delay(50);
    }

#endif // INPUT_GYRO

  }
}

void resetPlayground()
{
  figureIsDropingDown = false;

  for (byte y = 0; y < SCREEN_HEIGHT; y++)
  {
    for (byte x = 0; x < SCREEN_WIDTH; x++)
    {
      setPointState(x, y, B00110000);
    }
  }

  for (byte y = 0; y < PLAYGROUND_HEIGHT; y++)
  {
    for (byte x = 0; x < PLAYGROUND_WIDTH; x++)
    {
      setPointState(((SCREEN_WIDTH - PLAYGROUND_WIDTH) / 2) + x, 1 + y, 0);
    }
  }
}

void startNewGame(bool withWait)
{
  if (digitalRead(PIN_SWITCH) == LOW)
  {
#ifdef INPUT_KEYS

    if (withWait == true)
    {
      for (;;)
      {
        if (digitalRead(PIN_BUTTON_LEFT) == HIGH)
          break;
        else if (digitalRead(PIN_BUTTON_RIGHT) == HIGH)
          break;
        else if (digitalRead(PIN_BUTTON_DOWN) == HIGH)
          break;
        else if (digitalRead(PIN_BUTTON_UP) == HIGH)
          break;
      }
    }

#endif // INPUT_KEYS
  }
  else
  {
#ifdef INPUT_GYRO

    delay(500);

    Wire.beginTransmission(MPU);
    Wire.write(0x3B);
    Wire.endTransmission(false);
    Wire.requestFrom(MPU, 12, true);

    accelerometerInit.x = Wire.read() << 8 | Wire.read();
    accelerometerInit.y = Wire.read() << 8 | Wire.read();
    accelerometerInit.z = Wire.read() << 8 | Wire.read();

    gyroscopeInit.x = Wire.read() << 8 | Wire.read();
    gyroscopeInit.y = Wire.read() << 8 | Wire.read();
    gyroscopeInit.z = Wire.read() << 8 | Wire.read();

#endif // INPUT_GYRO
  }

  resetPlayground();
  createNewFigure();
}

bool createNewFigure()
{
  figureIsDropingDown = false;

  figure.x = (SCREEN_WIDTH / 2) - (FIGURE_SIZE / 2);
  figure.y = SCREEN_HEIGHT - (FIGURE_SIZE / 2);
  figure.type = random(0, NUMBER_OF_FIGURES);
  figure.color = random(0, 255);
  figure.rotation = 0;

  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      figure.matrix[x][y] = false;
    }
  }

  for (byte pixel = 0; pixel < FIGURE_SIZE; pixel++)
  {
    byte offsetX = 1 + figures[figure.type][figure.rotation][pixel][0];
    byte offsetY = 1 + figures[figure.type][figure.rotation][pixel][1];
    figure.matrix[offsetX][offsetY] = true;
  }

  if (figureFitsOnPlayground() == true)
  {
    showFigure();
    return true;
  }
  else
  {
    return false;
  }
}

void hideFigure()
{
  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      if (figure.matrix[x][y] == true)
        setPointState(figure.x + x, figure.y + y, 0);
    }
  }
}

void showFigure()
{
  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      if (figure.matrix[x][y] == true)
        setPointState(figure.x + x, figure.y + y, figure.color);
    }
  }
}

bool figureFitsOnPlayground()
{
  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      if ((figure.matrix[x][y] == true) && (getPointState(figure.x + x, figure.y + y) != 0))
        return false;
    }
  }

  return true;
}

void moveFigureLeft()
{
  bool canMove = true;

  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      if (figure.matrix[x][y] == true)
      {
        byte screenX = figure.x + x;
        byte screenY = figure.y + y;

        if (getPointState(screenX - 1, screenY) != 0)
        {
          canMove = false;
          break;
        }

        break;
      }
    }

    if (canMove == false)
      break;
  }

  if (canMove == true)
  {
    hideFigure();
    figure.x--;
    showFigure();
  }
}

void moveFigureRight()
{
  bool canMove = true;

  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      if (figure.matrix[FIGURE_SIZE - 1 - x][y] == true)
      {
        byte screenX = figure.x + FIGURE_SIZE - 1 - x;
        byte screenY = figure.y + y;

        if (getPointState(screenX + 1, screenY) != 0)
        {
          canMove = false;
          break;
        }

        break;
      }
    }

    if (canMove == false)
      break;
  }

  if (canMove == true)
  {
    hideFigure();
    figure.x++;
    showFigure();
  }
}

void turnFigure()
{
  hideFigure();

  figure.rotation = (figure.rotation == 0) ? 3 : figure.rotation - 1;

  for (byte y = 0; y < FIGURE_SIZE; y++)
  {
    for (byte x = 0; x < FIGURE_SIZE; x++)
    {
      figure.matrix[x][y] = false;
    }
  }

  for (byte pixel = 0; pixel < FIGURE_SIZE; pixel++)
  {
    byte offsetX = 1 + figures[figure.type][figure.rotation][pixel][0];
    byte offsetY = 1 + figures[figure.type][figure.rotation][pixel][1];

    if (getPointState(figure.x + offsetX, figure.y + offsetY) != 0)
    {
      figure.rotation = (figure.rotation == 0) ? 3 : figure.rotation - 1;
      break;
    }
  }

  for (byte pixel = 0; pixel < FIGURE_SIZE; pixel++)
  {
    byte offsetX = 1 + figures[figure.type][figure.rotation][pixel][0];
    byte offsetY = 1 + figures[figure.type][figure.rotation][pixel][1];
    figure.matrix[offsetX][offsetY] = true;
  }

  showFigure();
}

void dropFigure()
{
  figureIsDropingDown = true;
}

bool moveFigureDown()
{
  bool canMove = true;

  for (byte x = 0; x < FIGURE_SIZE; x++)
  {
    for (byte y = 0; y < FIGURE_SIZE; y++)
    {
      if (figure.matrix[x][y] == true)
      {
        byte screenX = figure.x + x;
        byte screenY = figure.y + y;

        if (getPointState(screenX, screenY - 1) != 0)
        {
          canMove = false;
          break;
        }

        break;
      }
    }

    if (canMove == false)
      break;
  }

  if (canMove == true)
  {
    hideFigure();
    figure.y--;
    showFigure();
  }
  else
  {
    delay(200);

#ifdef SOUND
    tone(PIN_PIEZO, 500);
    delay(20);
    tone(PIN_PIEZO, 1000, 40);
#endif

    removeFullLines();

    bool newFigureCreated = createNewFigure();
    if (newFigureCreated == true)
    {
      showFigure();
    }
    else
    {
      delay(200);
      return false;
    }

    delay(100);
  }

  return true;
}

void removeFullLines()
{
  for (byte y = PLAYGROUND_BOTTOM; y <= PLAYGROUND_TOP; y++)
  {
    for (;;)
    {
      bool lineIsFull = true;

      for (byte x = PLAYGROUND_LEFT; x <= PLAYGROUND_RIGHT; x++)
      {
        if (getPointState(x, y) == 0)
        {
          lineIsFull = false;
        }
      }

      if (lineIsFull == false)
        break;

      for (byte x = PLAYGROUND_LEFT; x <= PLAYGROUND_RIGHT; x++)
        setPointState(x, y, 0);

      delay(500);

      for (byte y2 = y; y2 <= PLAYGROUND_TOP; y2++)
      {
        for (byte x = PLAYGROUND_LEFT; x <= PLAYGROUND_RIGHT; x++)
        {
          setPointState(x, y2, getPointState(x, y2 + 1));
          setPointState(x, y2 + 1, 0);
        }
      }

#ifdef SOUND
      tone(PIN_PIEZO, 1000);
      delay(20);
      tone(PIN_PIEZO, 1600);
      delay(10);
      tone(PIN_PIEZO, 1800);
      delay(20);
      tone(PIN_PIEZO, 1600);
      delay(10);
      tone(PIN_PIEZO, 1200, 40);
#endif
    }
  }
}

byte getPointState(byte x, byte y)
{
  if ((x < SCREEN_HEIGHT) && (y < SCREEN_HEIGHT))
    return screen[x][y];
  else
    return false;
}

void setPointState(byte x, byte y, byte state)
{
  if (y < SCREEN_HEIGHT)
  {
    if (screen[x][y] != state)
    {
      screen[x][y] = state;

#ifdef OUTPUT_MAX

      byte deviceNumber, rowNumber, columnNumber;

      if (x < MATRIX_SIZE)
      {
        if (y < MATRIX_SIZE)
        {
          deviceNumber = 1;
          rowNumber = x;
          columnNumber = y;
        }
        else
        {
          deviceNumber = 0;
          rowNumber = x;
          columnNumber = y - MATRIX_SIZE;
        }
      }
      else
      {
        if (y < MATRIX_SIZE)
        {
          deviceNumber = 3;
          rowNumber = x - MATRIX_SIZE;
          columnNumber = y;
        }
        else
        {
          deviceNumber = 2;
          rowNumber = x - MATRIX_SIZE;
          columnNumber = y - MATRIX_SIZE;
        }
      }

      matrix.setLed(deviceNumber + 1, rowNumber, columnNumber, (state == 0) ? false : true);

#endif // OUTPUT_MAX

#ifdef OUTPUT_TFT

      tft.stroke(
          ((state >> 4) & B11) * 80,
          ((state >> 2) & B11) * 80,
          ((state)      & B11) * 80);
      byte coordinateX = x * PIXEL_SIZE + (PIXEL_SIZE - 1);
      byte coordinateY = y * PIXEL_SIZE + (PIXEL_SIZE - 1);
      tft.rect(
          SCREEN_HEIGHT_IN_PIXEL - coordinateY,
          SCREEN_WIDTH_IN_PIXEL - coordinateX,
          PIXEL_SIZE - 1,
          PIXEL_SIZE - 1);
      tft.rect(
          SCREEN_HEIGHT_IN_PIXEL - coordinateY + 1,
          SCREEN_WIDTH_IN_PIXEL - coordinateX + 1,
          PIXEL_SIZE - 3,
          PIXEL_SIZE - 3);
    }

#endif // OUTPUT_TFT
  }
}
Kategorien: Ideen

0 Kommentare

Schreibe einen Kommentar

Avatar-Platzhalter

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert