commit 7ae1d2a7248c86956fd1c1cd32805accbc391b06 Author: Baipyrus Date: Mon Nov 14 13:08:14 2022 +0100 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fe04384 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +obj/* +bin/* +.idea/* \ No newline at end of file diff --git a/ConsoleTetris.csproj b/ConsoleTetris.csproj new file mode 100644 index 0000000..8f38e51 --- /dev/null +++ b/ConsoleTetris.csproj @@ -0,0 +1,13 @@ + + + + Exe + net6.0 + + true + win10-x64 + true + true + + + diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..e0951dd --- /dev/null +++ b/Program.cs @@ -0,0 +1,686 @@ +using System; +using System.Threading; +using System.Diagnostics; + +namespace Tetris { + public static class Tetris { + // Static variables + 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 } }, // 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 } }, // 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 } }, // 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 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 int selectionIndex, frameCount; + private static int score, bounds; + public static bool selection; + + public static void Main() { + // Initialize board + if (!hasStarted) { + initBoard(board); + hasStarted = true; + } + + // Initialize stopwatch and Console Color + Console.ForegroundColor = ConsoleColor.White; + Stopwatch stopwatch = new(); + + // Outer loop for restarting the Game + startOver = true; + while (startOver) { + // Reset loop + Console.Clear(); + + // 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; + gameOver = false; + ConsoleColor[,] copy = new ConsoleColor[10, 21]; + while (!gameOver) { + // Handle Keypresses + if (Console.KeyAvailable && keyHandler()) { + EndGame(); + break; + } + + // Move Player down every few frames + 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 + frameCount = (frameCount+1)%(FPS*4); + + // Fix framerate + int elapsed = (int)stopwatch.ElapsedMilliseconds; + if (elapsed < 1000/FPS) + Thread.Sleep(1000/FPS - elapsed); + } + + // If Player has lost but not yet quit + if (startOver) { + // Start loop to select next step in + frameCount = 0; + selection = true; + while (selection) { + // Display Options and Cursor on Matrix + 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) { + frameCount = 0; + switch (Console.ReadKey(true).Key) { + case ConsoleKey.Escape: + // Exit Game + selection = false; + startOver = false; + break; + case ConsoleKey.DownArrow: + // Increment index + if (selectionIndex < 1) + selectionIndex++; + break; + case ConsoleKey.UpArrow: + // Decrement index + if (selectionIndex > 0) + selectionIndex--; + break; + case ConsoleKey.Enter: + // Enter selected option + enterSelection(); + break; + } + } + + // Exit if no response for 30 secs + if (frameCount >= 300) { + selection = false; + startOver = false; + } + + // Wait for input + frameCount++; + Thread.Sleep(100); + } + } + } + } + + // Method to initialize the Game board + 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; + + // 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 + private static bool collisionCheck(int x, int y, int[][] block) { + foreach (int[] b in block) { + int nx = x + b[0], ny = y + b[1]; + if (nx is > 9 or < 0 || ny is > 20 or < 0 || !board[nx, ny].Equals(black)) + return true; + } + return false; + } + + // Method to place currently held block + private static void placeBlock() { + // Block placing + 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; + for (int j = board.GetLength(1)-1; j >= 0;) { + bool isRow = true; + for (int i = 0; i < board.GetLength(0); i++) + if (board[i, j].Equals(black)) { + isRow = false; + break; + } + if (isRow) { + rows++; + for (int i = j-1; i >= 0; i--) + 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--; + } + // Add to score + if (rows >= 4) { + int tetris = rows / 4; + if (tetris > 1) + score += 1200 * tetris; + else + score += 800; + } else if (rows == 3) + score += 400; + 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; + for (int i = 1; i < blocks.Length; i++) + if (blocks[i].Equals(next.positions)) { + blockIndex = i; + break; + } + + // Generate new player with given parameters + player = new( + blockIndex, + player.memPos, + player.memDis, + player.memCol + ); + // Generate new "next Block" with displaySelf State + next = new(true); + + // If new block doesnt collide with any other, keep playing (also re-enable swapping) + if (!collisionCheck(player.x, player.y, player.positions)) + canSwap = true; + // Otherwise reset game, because player has lost + else { + 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() { + // If key is pressed, handle logic + switch (Console.ReadKey(true).Key) { + case ConsoleKey.Escape: + // Exit program. + return true; + case ConsoleKey.Spacebar: + // Place block on lowest point at current x + placeDownBlock(); + break; + case ConsoleKey.DownArrow: + // Lower block by 1 on y-axis + moveDownOnce(); + break; + case ConsoleKey.LeftArrow: + // Move block to the left on the x axis + moveLeftSide(); + break; + case ConsoleKey.RightArrow: + // Move block to the right on the x axis + 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; + } + + // Method to choose what a given index has to do + public static void enterSelection() { + switch (selectionIndex) { + case 1: + selection = false; + startOver = false; + break; + case 0: + selection = false; + frameCount = 0; + break; + } + } + + // Depending on currently used block, save its width and height (they're always the same) + public static void calcBounds() { + int blockIndex = 0; + for (int i = 1; i < blocks.Length; i++) + if (blocks[i].Equals(player.positions)) { + blockIndex = i; + break; + } + switch (blockIndex) { + default: + bounds = 3; + break; + case 2: + bounds = 2; + break; + case 4: + bounds = 4; + break; + } + } + + // Adjust x-positions if necessary due to any previous movement + public static void adjustXPos() { + foreach (int[] b in player.positions) { + int nx = player.x + b[0]; + while (nx > 9) { + player.x--; + nx = player.x + b[0]; + } + while (nx < 0) { + player.x++; + nx = player.x + b[0]; + } + } + } + + // 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; + ConsoleColor tempCol1 = player.color; + ConsoleColor? tempCol2 = player.memCol; + // Generate new Player and assign saved variables + player = new(); + player.memPos = tempPos; + player.memCol = tempCol1; + if (tempMem != null && tempCol2 != null) { + player.positions = tempMem; + 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++) { + player.memDis[i] = new int[tempPos[i].Length]; + for (int j = 0; j < tempPos[i].Length; j++) + 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() { + foreach (int[] a in player.positions) { + int temp = a[0]; + a[0] = bounds - 1 - a[1]; + a[1] = temp; + } + } + + // Rotate current block counter-Clockwise + public static void rotateCounterClockwise() { + foreach (int[] a in player.positions) { + int temp = a[1]; + a[1] = bounds - 1 - a[0]; + a[0] = temp; + } + } + + // 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() { + // 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 + public static void EndGame() { + gameOver = true; + startOver = false; + } + + // Data-Class to store a player's variables in + private class Player { + public int[][] positions; + public int[][]? memPos; + public int[][]? memDis; + public ConsoleColor? memCol; + public ConsoleColor color; + public int x, y; + + // Constructor to generate new Player + public Player(bool displaySelf=false) { + int randInt = (new Random()).Next(7); + + if (displaySelf) { + int[][] tempPos = blocks[randInt]; + memDis = new int[tempPos.Length][]; + for (int i = 0; i < tempPos.Length; i++) { + memDis[i] = new int[tempPos[i].Length]; + for (int j = 0; j < tempPos[i].Length; j++) + memDis[i][j] = tempPos[i][j]; + } + } + + positions = blocks[randInt]; + color = colors[randInt]; + + int xB; + switch (randInt) { + default: + xB = 3; + break; + case 2: + xB = 2; + break; + case 4: + xB = 4; + break; + } + + x = 4 - xB / 2; + y = 0; + } + + // Constructor to carry over variables + public Player(int blockIndex, int[][]? _memPos, int[][]? _memDis, ConsoleColor? _memCol) { + memPos = _memPos; + memDis = _memDis; + memCol = _memCol; + + positions = blocks[blockIndex]; + color = colors[blockIndex]; + + int xB; + switch (blockIndex) { + default: + xB = 3; + break; + case 2: + xB = 2; + break; + case 4: + xB = 4; + break; + } + + x = 4 - xB / 2; + y = 0; + } + } + } +} \ No newline at end of file