Compare commits

...

4 Commits

Author SHA1 Message Date
26a238e4ab remove unnecessary files 2022-11-14 13:34:00 +01:00
cdeba9b904 initial merge for history 2022-11-14 13:31:09 +01:00
92095b6651 renaming 2022-11-14 13:21:31 +01:00
7ae1d2a724 initial commit 2022-11-14 13:08:14 +01:00
5 changed files with 309 additions and 239 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
obj/*
bin/*
obj/*
.idea/*

Binary file not shown.

537
Tetris.cs
View File

@ -1,151 +1,129 @@
using System;
using System.Threading;
using System.Diagnostics;
using rpi_rgb_led_matrix_sharp;
namespace Tetris {
public static class Tetris {
// Static variables
private static readonly Color[] colors = {
new(255, 0, 0), // Red
new(255, 127, 0), // Orange
new(255, 255, 0), // Yellow
new(0, 255, 0), // Green
new(0, 255, 255), // Cyan
new(0, 127, 255), // Blue
new(255, 0, 255) }; // Purple
private static readonly ConsoleColor[] colors = {
ConsoleColor.Red,
ConsoleColor.Gray,
ConsoleColor.Yellow,
ConsoleColor.Green,
ConsoleColor.Cyan,
ConsoleColor.Blue,
ConsoleColor.Magenta
};
private static readonly int[][][] blocks = {
new[] { new[] { 0, 0 }, new[] { 1, 0 }, new[] { 1, 1 }, new[] { 2, 1 } }, // Mirrored-Z
new[] { new[] { 0, 0 }, new[] { 1, 0 }, new[] { 1, 1 }, new[] { 2, 1 } }, // Spiegel-Z
new[] { new[] { 1, 0 }, new[] { 1, 1 }, new[] { 1, 2 }, new[] { 2, 2 } }, // Normal-L
new[] { new[] { 0, 0 }, new[] { 1, 0 }, new[] { 0, 1 }, new[] { 1, 1 } }, // Square
new[] { new[] { 0, 0 }, new[] { 1, 0 }, new[] { 0, 1 }, new[] { 1, 1 } }, // Viereck
new[] { new[] { 0, 1 }, new[] { 1, 1 }, new[] { 1, 0 }, new[] { 2, 0 } }, // Normal-Z
new[] { new[] { 2, 0 }, new[] { 2, 1 }, new[] { 2, 2 }, new[] { 2, 3 } }, // Line
new[] { new[] { 1, 0 }, new[] { 1, 1 }, new[] { 1, 2 }, new[] { 0, 2 } }, // Mirrored-L
new[] { new[] { 2, 0 }, new[] { 2, 1 }, new[] { 2, 2 }, new[] { 2, 3 } }, // Strich
new[] { new[] { 1, 0 }, new[] { 1, 1 }, new[] { 1, 2 }, new[] { 0, 2 } }, // Spiegel-L
new[] { new[] { 0, 1 }, new[] { 1, 1 }, new[] { 2, 1 }, new[] { 1, 2 } } }; // T-Block
// Global Variables
private static int FPS = 60;
private static Color[,] board = new Color[10, 21];
private static readonly Color black = new(0, 0, 0);
private static ConsoleColor[,] board = new ConsoleColor[10, 21];
private static readonly ConsoleColor black = ConsoleColor.Black;
private static bool canSwap = true, gameOver, startOver = true, hasStarted;
private static Player player = new(), next = new(true);
public static bool selection, ptbLogo = true;
public static int selectionIndex, frameCount;
private static int score, bounds;
// Matrix variables
private static RGBLedMatrix? _matrix;
private static RGBLedCanvas? _canvas;
// Method to initialize the Game
public static void Init(RGBLedMatrix matrix, RGBLedCanvas canvas) {
_matrix = matrix;
_canvas = canvas;
}
public static bool selection;
public static void Main() {
// Initialize RGBLedMatrix
RGBLedMatrix matrix;
RGBLedCanvas canvas;
if (_matrix != null && _canvas != null) {
matrix = _matrix;
canvas = _canvas;
} else {
matrix = new(
new RGBLedMatrixOptions {
HardwareMapping = "adafruit-hat",
LimitRefreshRateHz = 0,
GpioSlowdown = 4,
Brightness = 100,
ChainLength = 1,
ScanMode = 1,
Rows = 64,
Cols = 64,
});
canvas = matrix.CreateOffscreenCanvas();
// Initialize board
if (!hasStarted) {
initBoard(board);
hasStarted = true;
}
// Initialize stopwatch to measure calculation and display time
Stopwatch stopwatch = new();
// Initialize Console Color and Text-Font and -Color
RGBLedFont font = new("fonts/5x8.bdf");
// Initialize stopwatch and Console Color
Console.ForegroundColor = ConsoleColor.White;
Color col = new(255, 255, 255), grey = new(127, 127, 127);
Stopwatch stopwatch = new();
// Outer loop for restarting the Game
startOver = true;
while (startOver) {
// Reset loop
Console.Clear();
canvas.Clear();
canvas = matrix.SwapOnVsync(canvas);
// Draw line "in the middle"
Console.ForegroundColor = ConsoleColor.White;
for (int i = 0; i < 21; i++) {
Console.SetCursorPosition(10, i+1);
Console.Write("#");
}
Console.SetCursorPosition(12, 1);
Console.Write("Save:");
Console.SetCursorPosition(12, 6);
Console.Write("Next:");
// Initially draw board
for (int i = 0; i < board.GetLength(0); i++)
for (int j = 0; j < board.GetLength(1); j++)
if (!board[i, j].Equals(black)) {
Console.ForegroundColor = board[i, j];
Console.SetCursorPosition(i, j + 1);
Console.Write("O");
}
// Inner loop for actual Gameplay
score = 0;
// Initialize board
if (!hasStarted) {
initBoard();
hasStarted = true;
}
gameOver = false;
ConsoleColor[,] copy = new ConsoleColor[10, 21];
while (!gameOver) {
// Start of Frame
canvas.Clear();
Console.SetCursorPosition(0, 0);
Console.WriteLine($"Score: {score}");
// Handle Keypresses
if (Console.KeyAvailable && keyHandler()) {
EndGame();
break;
}
// Display board onto Matrix
for (int i = 0; i < board.GetLength(0); i++)
for (int j = 0; j < board.GetLength(1); j++) {
Color current = board[i, j];
if (!current.Equals(black))
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
canvas.SetPixel(i * 3 + k, j * 3 + l, current);
}
// Display player onto Matrix
foreach (var current in player.positions) {
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
canvas.SetPixel((player.x + current[0]) * 3 + k, (player.y + current[1]) * 3 + l, player.color);
}
// Draw line "in the middle"
for (int i = 0; i < 64; i++)
canvas.SetPixel(30, i, grey);
// Statically display stored item on the right side
canvas.DrawText(font, 38, 8, col, "Save:");
if (player.memDis != null && player.memCol != null)
foreach (int[] b in player.memDis)
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
canvas.SetPixel((14 + b[0]) * 3 + k, (4+ b[1]) * 3 + l, (Color)player.memCol);
// Statically display next item below the previous
canvas.DrawText(font, 38, 40, col, "Next:");
if (next.memDis != null)
foreach (int[] b in next.memDis)
for (int k = 0; k < 3; k++)
for (int l = 0; l < 3; l++)
canvas.SetPixel((14 + b[0]) * 3 + k, (15+ b[1]) * 3 + l, (Color)next.color);
// Move Player down every few frames
if (frameCount % 30 == 0 && collisionCheck(player.x, ++player.y, player.positions)) {
player.y--;
placeBlock();
if (frameCount % 30 == 0) {
initBoard(copy);
for (int i = 0; i < board.GetLength(0); i++)
for (int j = 0; j < board.GetLength(1); j++) {
if (copy[i, j] != board[i, j])
copy[i, j] = board[i, j];
}
// Display board onto Matrix
for (int i = 0; i < board.GetLength(0); i++)
for (int j = 0; j < board.GetLength(1); j++) {
ConsoleColor current = board[i, j];
if (current != copy[i, j]) {
string nchar = (current != black) ? "O" : " ";
Console.ForegroundColor = board[i, j];
Console.SetCursorPosition(i, j + 1);
Console.Write(nchar);
}
}
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write(" ");
}
if (collisionCheck(player.x, ++player.y, player.positions)) {
player.y--;
placeBlock();
}
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
}
// End of Frame
canvas = matrix.SwapOnVsync(canvas);
frameCount = (frameCount+1)%(FPS*4);
// Fix framerate
@ -161,14 +139,12 @@ namespace Tetris {
selection = true;
while (selection) {
// Display Options and Cursor on Matrix
canvas.Clear();
canvas.DrawText(font, 0, 8, col, "Game Over!");
canvas.DrawText(font, 0, 24, col, " ) Weiter");
canvas.DrawText(font, 0, 32, col, " ) Beenden");
canvas.DrawText(font, 0, 48, col, $"Score: {score}");
// Cursor is a '#' at (0, 24 + i * 8) where i may be [0, 1]
canvas.DrawText(font, 0, 24+selectionIndex*8, col, "#");
canvas = matrix.SwapOnVsync(canvas);
Console.WriteLine("Game Over!");
Console.WriteLine();
Console.WriteLine(" ) Weiter!");
Console.WriteLine(" ) Beenden");
Console.WriteLine();
Console.WriteLine($"Score: {score}");
// Handle key presses for continue game or exit
if (Console.KeyAvailable) {
@ -211,45 +187,43 @@ namespace Tetris {
}
// Method to initialize the Game board
private static void initBoard() {
private static void initBoard(ConsoleColor[,] _board) {
for (int i = 0; i < board.GetLength(0); i++)
for (int j = 0; j < board.GetLength(1); j++)
board[i, j] = black;
if (ptbLogo) {
// P
for (int i = 0; i < 8; i++)
board[1, 13 + i] = colors[4];
board[2, 13] = colors[4];
board[3, 13] = colors[4];
board[4, 14] = colors[4];
board[4, 15] = colors[4];
board[2, 16] = colors[4];
board[3, 16] = colors[4];
// T
for (int i = 0; i < 8; i++)
board[5, 11 + i] = colors[5];
board[3, 11] = colors[5];
board[4, 11] = colors[5];
board[6, 11] = colors[5];
board[7, 11] = colors[5];
// B
for (int i = 0; i < 8; i++)
board[6, 13 + i] = colors[4];
board[7, 13] = colors[4];
board[8, 13] = colors[4];
board[9, 14] = colors[4];
board[9, 15] = colors[4];
board[7, 16] = colors[4];
board[8, 16] = colors[4];
board[9, 17] = colors[4];
board[9, 18] = colors[4];
board[9, 19] = colors[4];
board[7, 20] = colors[4];
board[8, 20] = colors[4];
}
_board[i, j] = black;
// P
for (int i = 0; i < 8; i++)
_board[1, 13 + i] = colors[4];
_board[2, 13] = colors[4];
_board[3, 13] = colors[4];
_board[4, 14] = colors[4];
_board[4, 15] = colors[4];
_board[2, 16] = colors[4];
_board[3, 16] = colors[4];
// T
for (int i = 0; i < 8; i++)
_board[5, 11 + i] = colors[5];
_board[3, 11] = colors[5];
_board[4, 11] = colors[5];
_board[6, 11] = colors[5];
_board[7, 11] = colors[5];
// B
for (int i = 0; i < 8; i++)
_board[6, 13 + i] = colors[4];
_board[7, 13] = colors[4];
_board[8, 13] = colors[4];
_board[9, 14] = colors[4];
_board[9, 15] = colors[4];
_board[7, 16] = colors[4];
_board[8, 16] = colors[4];
_board[9, 17] = colors[4];
_board[9, 18] = colors[4];
_board[9, 19] = colors[4];
_board[7, 20] = colors[4];
_board[8, 20] = colors[4];
}
// Method to check whether a block at given coordinates collides with any other block or border
@ -265,8 +239,14 @@ namespace Tetris {
// Method to place currently held block
private static void placeBlock() {
// Block placing
foreach (int[] current in player.positions)
board[player.x + current[0], player.y + current[1]] = player.color;
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
int x = player.x + current[0];
int y = player.y + current[1];
board[x, y] = player.color;
Console.SetCursorPosition(x, y + 1);
Console.Write("O");
}
// Count completed rows
int rows = 0;
@ -280,8 +260,12 @@ namespace Tetris {
if (isRow) {
rows++;
for (int i = j-1; i >= 0; i--)
for (int k = 0; k < board.GetLength(0); k++)
for (int k = 0; k < board.GetLength(0); k++) {
board[k, i + 1] = board[k, i];
Console.ForegroundColor = board[k, i];
Console.SetCursorPosition(k, i + 2);
Console.Write((board[k, i].Equals(black)) ? " " : "O");
}
continue;
}
j--;
@ -298,6 +282,19 @@ namespace Tetris {
else
score += 100 * rows;
Console.ForegroundColor = ConsoleColor.White;
Console.SetCursorPosition(0, 0);
Console.WriteLine($"Score: {score}");
// Remove next from Matrix
if (next.memDis != null) {
Console.ForegroundColor = black;
foreach (int[] b in next.memDis) {
Console.SetCursorPosition(13 + b[0], 7 + b[1]);
Console.Write(" ");
}
}
// Generating new Block
int blockIndex = 0;
@ -322,10 +319,19 @@ namespace Tetris {
canSwap = true;
// Otherwise reset game, because player has lost
else {
hasStarted = false;
gameOver = true;
initBoard(board);
player = new();
}
// Remove next from Matrix
if (next.memDis != null) {
Console.ForegroundColor = next.color;
foreach (int[] b in next.memDis) {
Console.SetCursorPosition(13 + b[0], 7 + b[1]);
Console.Write("O");
}
}
}
private static bool keyHandler() {
@ -351,16 +357,48 @@ namespace Tetris {
moveRightSide();
break;
case ConsoleKey.A:
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
// Rotate block counter-clockwise
calcBounds();
rotateCounterClockwise();
adjustXPos();
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
break;
case ConsoleKey.S:
// Swap current block with memory
swapMemory();
break;
case ConsoleKey.D:
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
// Rotate block clockwise
calcBounds();
rotateClockwise();
adjustXPos();
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
break;
}
return false;
@ -381,7 +419,7 @@ namespace Tetris {
}
// Depending on currently used block, save its width and height (they're always the same)
private static void calcBounds() {
public static void calcBounds() {
int blockIndex = 0;
for (int i = 1; i < blocks.Length; i++)
if (blocks[i].Equals(player.positions)) {
@ -402,7 +440,7 @@ namespace Tetris {
}
// Adjust x-positions if necessary due to any previous movement
private static void adjustXPos() {
public static void adjustXPos() {
foreach (int[] b in player.positions) {
int nx = player.x + b[0];
while (nx > 9) {
@ -419,25 +457,35 @@ namespace Tetris {
// Swap current block with the one in memory
public static void swapMemory() {
if (canSwap) {
// Remove memory from Matrix
if (player.memDis != null && player.memCol != null) {
Console.ForegroundColor = black;
foreach (int[] b in player.memDis) {
Console.SetCursorPosition(13 + b[0], 2 + b[1]);
Console.Write(" ");
}
}
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
// Save all data
int[][] tempPos = player.positions;
int[][]? tempMem = player.memPos;
Color tempCol1 = player.color;
Color? tempCol2 = player.memCol;
// Generating new Block
int blockIndex = 0;
for (int i = 1; i < blocks.Length; i++)
if (blocks[i].Equals(next.positions)) {
blockIndex = i;
break;
}
ConsoleColor tempCol1 = player.color;
ConsoleColor? tempCol2 = player.memCol;
// Generate new Player and assign saved variables
player = new(blockIndex, tempPos, tempCol1);
player = new();
player.memPos = tempPos;
player.memCol = tempCol1;
if (tempMem != null && tempCol2 != null) {
player.positions = tempMem;
player.color = (Color)tempCol2;
} else
next = new(true);
player.color = (ConsoleColor)tempCol2;
}
// Actually clone the content of block in memory to be displayed without rotating later
player.memDis = new int[tempPos.Length][];
for (int i = 0; i < tempPos.Length; i++) {
@ -446,84 +494,117 @@ namespace Tetris {
player.memDis[i][j] = tempPos[i][j];
}
canSwap = false;
// Place memory onto Matrix
if (player.memDis != null && player.memCol != null) {
Console.ForegroundColor = (ConsoleColor)player.memCol;
foreach (int[] b in player.memDis) {
Console.SetCursorPosition(13 + b[0], 2 + b[1]);
Console.Write("O");
}
}
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
}
}
// Rotate current block Clockwise
public static void rotateClockwise() {
Player memory = prepareRotation();
foreach (int[] a in player.positions) {
int temp = a[0];
a[0] = bounds - 1 - a[1];
a[1] = temp;
}
finishRotation(memory);
}
// Rotate current block counter-Clockwise
public static void rotateCounterClockwise() {
Player memory = prepareRotation();
foreach (int[] a in player.positions) {
int temp = a[1];
a[1] = bounds - 1 - a[0];
a[0] = temp;
}
finishRotation(memory);
}
// Method to remember current positions of chosen block without memory reference & calculate bounds of it
private static Player prepareRotation() {
int[][] memory = new int[player.positions.Length][];
for (int i = 0; i < player.positions.Length; i++) {
memory[i] = new int[player.positions[i].Length];
for (int j = 0; j < player.positions[i].Length; j++)
memory[i][j] = player.positions[i][j];
}
calcBounds();
return new Player(player.x, player.y, memory);
}
// Method to adjust x-Position based on bounds of chosen block and width of Matrix, then reset rotation if is colliding with others
private static void finishRotation(Player memory) {
adjustXPos();
if (collisionCheck(player.x, player.y, player.positions)) {
player.x = memory.x;
player.y = memory.y;
for (int i = 0; i < player.positions.Length; i++)
for (int j = 0; j < player.positions[i].Length; j++) {
player.positions[i][j] = memory.positions[i][j];
}
}
}
// Move current block to the right
public static void moveRightSide() {
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write(" ");
}
if (collisionCheck(++player.x, player.y, player.positions))
player.x--;
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
}
// Move current block to the left
public static void moveLeftSide() {
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write(" ");
}
if (collisionCheck(--player.x, player.y, player.positions))
player.x++;
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
}
// Move current block downwards
public static void moveDownOnce() {
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write(" ");
}
if (collisionCheck(player.x, ++player.y, player.positions))
player.y--;
// Place player onto Matrix
Console.ForegroundColor = player.color;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write("O");
}
}
// Method to be called when the button for placing a block is pressed
public static void placeDownBlock() {
if (!gameOver) {
// Move block to the lowest possible point
while (!collisionCheck(player.x, ++player.y, player.positions)) { }
player.y--;
// Place the block
placeBlock();
// Remove player from Matrix
Console.ForegroundColor = black;
foreach (int[] current in player.positions) {
Console.SetCursorPosition(player.x + current[0], player.y + current[1] + 1);
Console.Write(" ");
}
// Move block to the lowest possible point
while (!collisionCheck(player.x, ++player.y, player.positions)) { }
player.y--;
// Place the block
placeBlock();
}
// Method to end the Game
@ -537,8 +618,8 @@ namespace Tetris {
public int[][] positions;
public int[][]? memPos;
public int[][]? memDis;
public Color? memCol;
public Color color;
public ConsoleColor? memCol;
public ConsoleColor color;
public int x, y;
// Constructor to generate new Player
@ -557,41 +638,33 @@ namespace Tetris {
positions = blocks[randInt];
color = colors[randInt];
int xB;
switch (randInt) {
default:
xB = 3;
break;
case 2:
xB = 2;
break;
case 4:
xB = 4;
break;
}
initSelf(randInt);
x = 4 - xB / 2;
y = 0;
}
// Constructor to carry over variables
public Player(int blockIndex, int[][]? _memPos, int[][]? _memDis, Color? _memCol) {
public Player(int blockIndex, int[][]? _memPos, int[][]? _memDis, ConsoleColor? _memCol) {
memPos = _memPos;
memDis = _memDis;
memCol = _memCol;
positions = blocks[blockIndex];
color = colors[blockIndex];
initSelf(blockIndex);
}
// Same difference
public Player(int blockIndex, int[][] _memPos, Color _memCol) {
positions = blocks[blockIndex];
color = colors[blockIndex];
memPos = _memPos;
memCol = _memCol;
initSelf(blockIndex);
}
// Constructor to use Player Object as a simple memory
public Player(int _x, int _y, int[][] _positions) {
x = _x;
y = _y;
positions = _positions;
}
public void initSelf(int blockIndex) {
int xB;
switch (blockIndex) {
default:

View File

@ -1,15 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Reference Include="RGBLedMatrix.dll" />
</ItemGroup>
<PropertyGroup>
<MainEntryPoint>Tetris.Tetris</MainEntryPoint>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<!-- <PublishSingleFile>true</PublishSingleFile>-->
<SelfContained>true</SelfContained>
<RuntimeIdentifier>linux-arm64</RuntimeIdentifier>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
<PublishReadyToRun>true</PublishReadyToRun>
<IncludeAllContentForSelfExtract>true</IncludeAllContentForSelfExtract>
</PropertyGroup>

Binary file not shown.