RPI_Matrix/RPI-Matrix.cs

464 lines
20 KiB
C#
Raw Permalink Normal View History

2022-10-18 05:49:03 +00:00
using System;
2022-10-20 12:28:10 +00:00
using System.IO;
using System.Linq;
2022-10-18 05:49:03 +00:00
using System.Threading;
using System.Collections.Generic;
2022-10-18 05:49:03 +00:00
using rpi_rgb_led_matrix_sharp;
namespace RPI_Matrix {
2022-10-18 07:05:53 +00:00
public static class RPI_Matrix {
2022-10-21 06:21:34 +00:00
// Initialize variables
2022-11-07 11:08:28 +00:00
private static int debugIndex;
private static bool debugMenu, debugSelect;
private static List<string> gamepadListeners = new();
private static int selectionIndex, keypressFrame, frameCount;
2022-10-21 06:21:34 +00:00
private static bool hasSelected, isInGame, isTetris, isFlappy, isSnake;
2023-03-29 09:57:53 +00:00
private static bool selection = true, isFocused, focusHeight, focusGravity, focusJump, focusLength;
2022-11-16 09:44:55 +00:00
2022-11-30 07:37:00 +00:00
public static void Main(string[] args) {
2022-10-21 06:21:34 +00:00
// Connect to RGB LED Matrix
2022-10-18 05:49:03 +00:00
RGBLedMatrix matrix = new(
new RGBLedMatrixOptions {
2022-11-30 07:37:00 +00:00
GpioSlowdown = 5,
ChainLength = 3,
Parallel = 3,
2022-10-18 05:49:03 +00:00
Rows = 64,
Cols = 64,
});
RGBLedCanvas canvas = matrix.CreateOffscreenCanvas();
2022-10-18 07:05:53 +00:00
2022-10-21 06:21:34 +00:00
// Initialize Games
2022-10-18 07:05:53 +00:00
SnakeGame.SnakeGame.Init(matrix, canvas);
FlappyBird.FlappyBird.Init(matrix, canvas);
2022-10-18 13:31:29 +00:00
Tetris.Tetris.Init(matrix, canvas);
2022-10-20 12:28:10 +00:00
2022-10-21 06:21:34 +00:00
// Start listening and interpreting Gamepad input in parallel
new Thread(InterpretGamepadInput).Start();
2022-10-21 06:21:34 +00:00
// Initialize Text-Font and -Color
2022-11-30 07:37:00 +00:00
RGBLedFont font = new("fonts/9x18B.bdf");
2022-10-18 05:49:03 +00:00
Color col = new(255, 255, 255);
2022-10-21 06:21:34 +00:00
// Game Menu
2022-10-20 12:28:10 +00:00
while (true) {
2022-10-21 06:21:34 +00:00
// Restart loop
2022-10-18 07:05:53 +00:00
Console.Clear();
2022-10-20 12:28:10 +00:00
selection = true;
2022-10-21 06:21:34 +00:00
// Game selection Menu
2022-10-18 05:49:03 +00:00
while (selection) {
2022-10-21 06:21:34 +00:00
// Display Games and Cursor on Matrix
2022-10-20 12:28:10 +00:00
canvas.Clear();
2022-11-30 07:37:00 +00:00
canvas.DrawText(font, 0, 18, col, "Spielauswahl:");
canvas.DrawText(font, 0, 54, col, " ) SnakeGame");
canvas.DrawText(font, 0, 72, col, " ) FlappyBird");
canvas.DrawText(font, 0, 90, col, " ) Tetris");
2023-04-14 09:13:38 +00:00
canvas.DrawText(font, 0, 126, col, " ) Einstellungen");
2022-11-30 07:37:00 +00:00
canvas.DrawText(font, 0, 144, col, " ) Beenden");
2022-11-07 11:08:28 +00:00
// Cursor is a '#' at (0, 24 + i * 8) where i may be [0, 1, 2, 3, 5]
2022-11-07 12:27:02 +00:00
int nx = selectionIndex + ((selectionIndex >= 3) ? 1 : 0);
2022-11-30 07:37:00 +00:00
canvas.DrawText(font, 0, 54 + nx * 18, col, "#");
2022-10-20 12:28:10 +00:00
canvas = matrix.SwapOnVsync(canvas);
2022-10-18 05:49:03 +00:00
// Handle key presses for selecting a game
if (Console.KeyAvailable) {
keypressFrame = frameCount;
switch (Console.ReadKey(true).Key) {
2022-10-20 12:28:10 +00:00
case ConsoleKey.Escape:
return;
case ConsoleKey.UpArrow:
if (!isInGame && selectionIndex > 0)
selectionIndex--;
break;
case ConsoleKey.DownArrow:
2022-11-07 11:08:28 +00:00
if (!isInGame && selectionIndex < 4)
2022-10-20 12:28:10 +00:00
selectionIndex++;
break;
case ConsoleKey.Enter:
if (!isInGame)
hasSelected = true;
2022-10-18 05:49:03 +00:00
break;
2022-10-20 12:28:10 +00:00
}
}
2022-10-21 06:21:34 +00:00
// Use selected index to start a Game
2022-10-20 12:28:10 +00:00
if (hasSelected) {
hasSelected = false;
switch (selectionIndex) {
case 0:
2022-10-21 06:21:34 +00:00
// Start SnakeGame in Singleplayer
2023-03-29 06:15:03 +00:00
playSnakeGame();
2022-10-20 12:28:10 +00:00
break;
case 1:
2022-10-21 06:21:34 +00:00
// Start FlappyBird
2022-10-20 12:28:10 +00:00
playFlappyBird();
2022-10-18 05:49:03 +00:00
break;
2022-10-20 12:28:10 +00:00
case 2:
2022-10-21 06:21:34 +00:00
// Start Tetris
2022-10-20 12:28:10 +00:00
playTetris();
2022-10-18 05:49:03 +00:00
break;
2022-10-20 12:28:10 +00:00
case 3:
2022-11-07 11:08:28 +00:00
// Open debug Menu
openDebugMenu(matrix, canvas);
break;
case 4:
2022-10-21 06:21:34 +00:00
// Exit program
2022-10-20 12:28:10 +00:00
Environment.Exit(0);
2022-10-18 05:49:03 +00:00
break;
}
}
2022-10-21 06:21:34 +00:00
// Reset Game identification variables after exiting
2022-10-20 12:28:10 +00:00
isInGame = false;
isTetris = false;
isFlappy = false;
isSnake = false;
2022-10-21 06:21:34 +00:00
// If player is absent for 15 seconds, start SnakeGame in Bot Mode
2022-10-18 05:49:03 +00:00
if (frameCount >= keypressFrame + 150) {
keypressFrame = frameCount;
2022-10-20 12:28:10 +00:00
playSnakeGame("-b");
2022-10-18 05:49:03 +00:00
}
2022-10-21 06:21:34 +00:00
// Wait for next frame and keep count
2022-10-18 05:49:03 +00:00
Thread.Sleep(100);
frameCount = (frameCount+1)%1000000;
}
}
}
2022-10-20 12:28:10 +00:00
2022-11-07 11:08:28 +00:00
private static void openDebugMenu(RGBLedMatrix matrix, RGBLedCanvas canvas) {
// Set identification variables and load settings
isInGame = true;
selection = false;
// Initialize Text-Font and -Color
2022-11-30 07:37:00 +00:00
RGBLedFont font = new("fonts/9x18B.bdf");
2022-11-07 11:08:28 +00:00
Color col = new(255, 255, 255);
// Game selection Menu
debugMenu = true;
while (debugMenu) {
// Display Games and Cursor on Matrix
canvas.Clear();
2022-11-30 07:37:00 +00:00
canvas.DrawText(font, 0, 18, col, "Tetris:");
canvas.DrawText(font, 0, 36, col, $" ) PTB Logo {(Tetris.Tetris.ptbLogo ? "I" : "O")}");
canvas.DrawText(font, 0, 54, col, "FlappyBird:");
canvas.DrawText(font, 0, 72, col, $" ) Höhe {FlappyBird.FlappyBird.spaceHeight}");
canvas.DrawText(font, 0, 90, col, $" ) Gravitation {FlappyBird.FlappyBird.gravity}");
canvas.DrawText(font, 0, 108, col, $" ) Sprung Ges. {FlappyBird.FlappyBird.jumpVel}");
2023-03-29 09:57:53 +00:00
canvas.DrawText(font, 0, 126, col, "SnakeGame");
canvas.DrawText(font, 0, 144, col, $" ) Basis Länge {SnakeGame.SnakeGame.baseLength}");
canvas.DrawText(font, 0, 180, col, " ) Zurück");
2022-11-16 09:44:55 +00:00
2022-11-07 11:08:28 +00:00
// Cursor is a '#' at (0, 24 + i * 8) where i may be [0, 2]
2022-11-16 09:44:55 +00:00
int nx = debugIndex;
if (debugIndex >= 1) nx++;
if (debugIndex >= 4) nx++;
2023-03-29 09:57:53 +00:00
if (debugIndex >= 5) nx++;
2022-11-16 09:44:55 +00:00
2022-11-30 07:37:00 +00:00
canvas.DrawText(font, 0, 36 + nx * 18, col, "#");
2022-11-07 11:08:28 +00:00
canvas = matrix.SwapOnVsync(canvas);
// Handle key presses for selecting a game
if (Console.KeyAvailable) {
switch (Console.ReadKey(true).Key) {
case ConsoleKey.Escape:
return;
case ConsoleKey.UpArrow:
if (debugIndex > 0)
debugIndex--;
break;
case ConsoleKey.DownArrow:
2023-03-29 09:57:53 +00:00
if (debugIndex < 5)
2022-11-07 11:08:28 +00:00
debugIndex++;
break;
case ConsoleKey.Enter:
debugSelect = true;
break;
}
}
// Use selected index to change a setting
if (debugSelect) {
debugSelect = false;
switch (debugIndex) {
case 0:
// Toggle Tetris PTB Logo
Tetris.Tetris.ptbLogo = !Tetris.Tetris.ptbLogo;
break;
case 1:
2022-11-16 09:44:55 +00:00
// Focus Height to be changed with arrow keys
focusHeight = true;
isFocused = true;
break;
case 2:
// Focus Gravity to be changed with arrow keys
focusGravity = true;
isFocused = true;
break;
case 3:
// Focus Jump to be changed with arrow keys
focusJump = true;
isFocused = true;
break;
case 4:
2023-03-29 09:57:53 +00:00
// Go back to menu
focusLength = true;
isFocused = true;
break;
case 5:
2022-11-07 11:08:28 +00:00
// Go back to menu
debugMenu = false;
break;
}
}
// Wait for next frame and keep count
2022-11-07 12:27:02 +00:00
if (debugMenu)
Thread.Sleep(100);
2022-11-07 11:08:28 +00:00
}
}
2023-03-29 06:15:03 +00:00
private static void playSnakeGame(string arg = "") {
2022-10-21 06:21:34 +00:00
// Set Game identification variables and start playing SnakeGame
2022-10-20 12:28:10 +00:00
isSnake = true;
isInGame = true;
selection = false;
SnakeGame.SnakeGame.players = gamepadListeners;
2022-10-20 12:28:10 +00:00
SnakeGame.SnakeGame.Main(new[] { arg });
}
private static void playFlappyBird() {
2022-10-21 06:21:34 +00:00
// Set Game identification variables and start playing FlappyBird
2022-10-20 12:28:10 +00:00
isFlappy = true;
isInGame = true;
selection = false;
FlappyBird.FlappyBird.Main();
}
private static void playTetris() {
2022-10-21 06:21:34 +00:00
// Set Game identification variables and start playing Tetris
2022-10-20 12:28:10 +00:00
isTetris = true;
isInGame = true;
selection = false;
Tetris.Tetris.Main();
}
private static void InterpretGamepadInput() {
// Read file "/dev/input/js*" in Raspbian for Raspberry 4 4GB.
2022-10-21 06:21:34 +00:00
// All inputs on a Gamepad are accessible from within here.
// Interpreting this data may only work in this program using a SNES Controller.
while (true) {
2023-03-29 06:38:53 +00:00
string dir = "/dev/input/";
IEnumerable<string> files = from retrieved in Directory.EnumerateFiles(dir)
where retrieved.Contains("js")
select retrieved;
foreach (string f in files) {
string name = f.Replace(dir, "");
if (!gamepadListeners.Contains(name)) {
gamepadListeners.Add(name);
new Thread(() => {
try {
2023-03-29 06:38:53 +00:00
WatchInputFile(dir, f);
}
catch (Exception) {
gamepadListeners.Remove(name);
}
}).Start();
}
}
Thread.Sleep(500);
}
}
2023-03-29 06:38:53 +00:00
private static void WatchInputFile(string dir, string file) {
FileInfo targetFile = new FileInfo(file);
2022-10-20 12:28:10 +00:00
using FileStream fs = targetFile.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
2022-10-21 06:21:34 +00:00
// Initialize Buffer (byte Array) to put read bytes into
2022-10-20 12:28:10 +00:00
byte[] buffer = new byte[1024];
while (fs.Read(buffer) > 0) {
2022-10-21 06:21:34 +00:00
// If inputs were registered, save frame #
2022-10-20 12:28:10 +00:00
keypressFrame = frameCount;
2022-10-21 06:21:34 +00:00
// Do the same in Tetris if it's in the 'GameOver' Screen
2022-10-20 12:28:10 +00:00
if (isTetris && Tetris.Tetris.selection)
Tetris.Tetris.frameCount = 0;
2022-10-21 06:21:34 +00:00
// And do it again in SnakeGame while it's playing.
2022-10-20 12:28:10 +00:00
else if (isSnake && !SnakeGame.SnakeGame.gameOver)
SnakeGame.SnakeGame.keypressFrame = SnakeGame.SnakeGame.frameCount;
2023-03-29 06:38:53 +00:00
2022-10-21 06:21:34 +00:00
// Convert Buffer to Hexadecimal, each Byte separated by a '-'
2023-03-29 06:38:53 +00:00
Delegate method = BitConverter.ToString(buffer, 4, 4) switch {
"01-00-01-00" => XButtonPressed,
"01-00-01-01" => AButtonPressed,
"01-00-01-02" => BButtonPressed,
"01-00-01-04" => LButtonPressed,
"01-00-01-05" => RButtonPressed,
"01-00-01-09" => StartButtonPressed,
"01-80-02-01" => UpArrowButtonPressed,
"FF-7F-02-01" => DownArrowPressed,
"01-80-02-00" => LeftArrowPressed,
"FF-7F-02-00" => RightArrowPressed,
2023-03-29 06:38:53 +00:00
_ => (string player) => {}
};
// Execute function mapped by buffer/button press as current player
method.DynamicInvoke(file.Replace(dir, ""));
}
}
2023-03-29 06:38:53 +00:00
private static void RightArrowPressed(string player) {
// Code for Arrow-Right Press
// If in SnakeGame, Steer the Snake to the right
if (isSnake) SnakeGame.SnakeGame.SteerRight(SnakeGame.SnakeGame.snakes[player]);
// If in Tetris, move current Block to the right
else if (isTetris) Tetris.Tetris.moveRightSide();
}
2023-03-29 06:38:53 +00:00
private static void LeftArrowPressed(string player) {
// Code for Arrow-Left Press
// If in SnakeGame, Steer the Snake to the left
if (isSnake) SnakeGame.SnakeGame.SteerLeft(SnakeGame.SnakeGame.snakes[player]);
// If in Tetris, move current Block to the left
else if (isTetris) Tetris.Tetris.moveLeftSide();
}
2023-03-29 06:38:53 +00:00
private static void DownArrowPressed(string player) {
// Code for Arrow-Down Press
// If in the selection screen, decrement index
if (!isInGame && selectionIndex < 4)
selectionIndex++;
// If in SnakeGame, Steer the Snake downwards
else if (isSnake) {
// Handle index in 'GameOver' Screen
if (SnakeGame.SnakeGame.selection && SnakeGame.SnakeGame.selectionIndex < 1)
SnakeGame.SnakeGame.selectionIndex++;
// If in SnakeGame, Steer the Snake downwards
else SnakeGame.SnakeGame.SteerDown(SnakeGame.SnakeGame.snakes[player]);
// If in Tetris and in Tetris' 'GameOver' Screen, decrement its index.
}
else if (isTetris)
// If in Tetris and in Tetris' 'GameOver' Screen, increment its index.
if (Tetris.Tetris.selection && Tetris.Tetris.selectionIndex < 1)
Tetris.Tetris.selectionIndex++;
// Otherwise, move Tetris piece down
else Tetris.Tetris.moveDownOnce();
else if (isFlappy && FlappyBird.FlappyBird.selection && FlappyBird.FlappyBird.selectionIndex < 1)
FlappyBird.FlappyBird.selectionIndex++;
else if (focusGravity && FlappyBird.FlappyBird.gravity > 0)
FlappyBird.FlappyBird.gravity -= 0.01f;
else if (focusHeight && FlappyBird.FlappyBird.spaceHeight > 2)
FlappyBird.FlappyBird.spaceHeight -= 1;
else if (focusJump && FlappyBird.FlappyBird.jumpVel > 0)
FlappyBird.FlappyBird.jumpVel -= 0.01f;
2023-03-29 09:57:53 +00:00
else if (focusLength && SnakeGame.SnakeGame.baseLength > 0)
SnakeGame.SnakeGame.baseLength -= 1;
else if (debugIndex < 5 && !isFocused)
debugIndex++;
}
2023-03-29 06:38:53 +00:00
private static void UpArrowButtonPressed(string player) {
// Code for Arrow-Up Press
// If in the selection screen, decrement index
if (!isInGame && selectionIndex > 0)
selectionIndex--;
else if (isSnake) {
// Handle index in 'GameOver' Screen
if (SnakeGame.SnakeGame.selection && SnakeGame.SnakeGame.selectionIndex > 0)
SnakeGame.SnakeGame.selectionIndex--;
// If in SnakeGame, Steer the Snake upwards
else SnakeGame.SnakeGame.SteerUp(SnakeGame.SnakeGame.snakes[player]);
// If in Tetris and in Tetris' 'GameOver' Screen, decrement its index.
}
else if (isTetris && Tetris.Tetris.selection && Tetris.Tetris.selectionIndex > 0)
Tetris.Tetris.selectionIndex--;
else if (isFlappy && FlappyBird.FlappyBird.selection && FlappyBird.FlappyBird.selectionIndex > 0)
FlappyBird.FlappyBird.selectionIndex--;
else if (focusGravity)
FlappyBird.FlappyBird.gravity += 0.01f;
else if (focusHeight && FlappyBird.FlappyBird.spaceHeight < 58)
FlappyBird.FlappyBird.spaceHeight += 1;
else if (focusJump)
FlappyBird.FlappyBird.jumpVel += 0.01f;
2023-03-29 09:57:53 +00:00
else if (focusLength)
SnakeGame.SnakeGame.baseLength += 1;
else if (debugIndex > 0 && !isFocused)
debugIndex--;
}
2023-03-29 06:38:53 +00:00
private static void StartButtonPressed(string player) {
// Code for Start-Button Press
// If in SnakeGame, end the Game and return to menu
if (isSnake)
SnakeGame.SnakeGame.EndGame();
else if (isFlappy) {
// If in FlappyBirds' 'ready' Screen, return to menu
if (!FlappyBird.FlappyBird.isReady)
FlappyBird.FlappyBird.stopPlaying = true;
// Otherwise go back to 'ready' Screen by ending the Game
else {
FlappyBird.FlappyBird.EndGame();
FlappyBird.FlappyBird.selection = false;
2022-10-20 12:28:10 +00:00
}
// If in Tetris, end the Game and return to menu
2022-10-20 12:28:10 +00:00
}
else if (isTetris)
Tetris.Tetris.EndGame();
}
2023-03-29 06:38:53 +00:00
private static void RButtonPressed(string player) {
// Code for R-Button Press
// If in Tetris, rotate the current Block Clockwise
if (isTetris) Tetris.Tetris.rotateClockwise();
}
2023-03-29 06:38:53 +00:00
private static void LButtonPressed(string player) {
// Code for L-Button Press
// If in Tetris, rotate the current Block counter-Clockwise
if (isTetris) Tetris.Tetris.rotateCounterClockwise();
}
2023-03-29 06:38:53 +00:00
private static void BButtonPressed(string player) {
// Code for B-Button Press
if (isFlappy) {
// Jump
if (FlappyBird.FlappyBird.isReady)
FlappyBird.FlappyBird.JumpOnce();
// Start Game
else FlappyBird.FlappyBird.isReady = true;
// Place the current Block in Tetris
}
else if (isTetris)
Tetris.Tetris.placeDownBlock();
else if (!isSnake) {
focusGravity = false;
2023-03-29 09:57:53 +00:00
focusLength = false;
focusHeight = false;
focusJump = false;
isFocused = false;
}
}
2023-03-29 06:38:53 +00:00
private static void AButtonPressed(string player) {
// Code for A-Button Press
// If not already in a Game, now ready up to select one.
if (!isInGame)
hasSelected = true;
// Select given option in Tetris' 'GameOver' Screen
else if (isSnake && SnakeGame.SnakeGame.selection)
SnakeGame.SnakeGame.enterSelection();
else if (isFlappy && FlappyBird.FlappyBird.selection)
FlappyBird.FlappyBird.enterSelection();
else if (isTetris && Tetris.Tetris.selection)
Tetris.Tetris.enterSelection();
else debugSelect = true;
}
2023-03-29 06:38:53 +00:00
private static void XButtonPressed(string player) {
// Code for X-Button Press
if (isTetris) Tetris.Tetris.swapMemory();
2022-10-20 12:28:10 +00:00
}
2022-10-18 05:49:03 +00:00
}
}