diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Components/WordleComponent.razor b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Components/WordleComponent.razor
new file mode 100644
index 0000000..9db207b
--- /dev/null
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Components/WordleComponent.razor
@@ -0,0 +1,146 @@
+
+
+
+
+
+
+ Korrekt!
+
+
+
+
+
+
+
+
+
+
+
+
+ @foreach(var guess in PreviousGuesses)
+ {
+
+ @for(int i = 0; i < ColumnCount; i++)
+ {
+ char guessedChar = char.ToUpper(guess[i]);
+
+ if(guessedChar == char.ToUpper(Secret[i]))
+ {
+
+ @guessedChar
+
+ }
+ else if (Secret.Contains(guessedChar, StringComparison.InvariantCultureIgnoreCase))
+ {
+
+ @guessedChar
+
+ }
+ else
+ {
+
+ @guessedChar
+
+ }
+ }
+
+ }
+
+
+
+
+
+
+
+
+ Hi
+
+
+ OK
+
+
+
+
+
+@code {
+ public string Input { get; set; } = string.Empty;
+
+ public bool hideAlert = true;
+
+ public MudTextField textField;
+
+ public List PreviousGuesses = new();
+ public List SecretList = new();
+
+ //Keine Ahnung wie das funktionert... Danke StackOverflow :)
+ ///
+ /// Eine Regexmaske die alle Strings matched die 0 bis 5 Buchstaben enthalten.
+ ///
+ //private IMask _inputMask = new RegexMask(@"^[A-Za-z]{0,5}$");
+
+ private bool found = false;
+
+ [Parameter]
+ public int ColumnCount { get; set; }
+
+ [Parameter]
+ public string? Secret { get; set; } = null;
+
+ [Parameter]
+ public IMask InputMask { get; set; } = new RegexMask(@"^[A-Za-z]{0,5}$");
+
+ [Parameter]
+ public Func? CheckInput { get; set; }
+
+ [Parameter]
+ public Func? GenerateSecret { get; set; }
+
+ public void Check4Enter(KeyboardEventArgs e)
+ {
+ if (e.Code == "Enter" || e.Code == "NumpadEnter")
+ {
+ if(found)
+ {
+ PlayAgain();
+ }
+ else
+ {
+ ButtonOnClick();
+ }
+ }
+ }
+
+ public void ButtonOnClick()
+ {
+ if (Input.Length != ColumnCount || found) return;
+
+ if (CheckInput != null && !CheckInput(Input))
+ return; // TODO: Eingabe ungültig!
+
+ if (Secret == null && GenerateSecret != null)
+ Secret = GenerateSecret();
+
+ PreviousGuesses.Add(Input);
+ if(Secret!.Equals(Input, StringComparison.InvariantCultureIgnoreCase))
+ {
+ found = true;
+ textField.Clear();
+ hideAlert = false;
+ }
+ else
+ {
+ textField.Clear();
+ }
+ }
+
+ public void PlayAgain()
+ {
+ PreviousGuesses.Clear();
+ hideAlert = true;
+ found = false;
+
+ if (GenerateSecret != null)
+ Secret = GenerateSecret();
+ }
+}
diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Ast.cs b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Ast.cs
new file mode 100644
index 0000000..c0577f2
--- /dev/null
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Ast.cs
@@ -0,0 +1,31 @@
+namespace GottfriedsNackenWebseite.Numberle;
+
+public abstract class AstNode
+{
+
+}
+
+public class AstBinaryExpression : AstNode
+{
+ public AstNode Left;
+ public AstNode Right;
+
+ public string Operator;
+
+ public AstBinaryExpression(string @operator, AstNode left, AstNode right)
+ {
+ Operator = @operator;
+ Left = left;
+ Right = right;
+ }
+}
+
+public class AstNumericLiteral : AstNode
+{
+ public int Value;
+
+ public AstNumericLiteral(int value)
+ {
+ Value = value;
+ }
+}
diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Parser.cs b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Parser.cs
new file mode 100644
index 0000000..6b33488
--- /dev/null
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Parser.cs
@@ -0,0 +1,104 @@
+namespace GottfriedsNackenWebseite.Numberle;
+
+public class Parser
+{
+ private string _input = string.Empty;
+
+ private readonly Tokenizer _tokenizer = new();
+
+ private Token _lookahead;
+
+ private TokenType NextTokenType => _lookahead.Type;
+
+ public AstNode Parse(string input)
+ {
+ _input = input;
+ _tokenizer.Init(_input);
+
+ // Prime the tokenizer to obtain the first token
+ // which is our lookahead. The lookahead is used
+ // for predictive parsing.
+ _lookahead = _tokenizer.GetNextToken();
+
+ // Parse recursively starting from the main entry point, the Program:
+
+ return ParseEqualityExpression();
+ }
+
+ private AstNode ParseBinaryExpression(Func builder, TokenType operatorType)
+ {
+ AstNode left = builder();
+
+ while (_lookahead.Type == operatorType)
+ {
+ string @operator = (string)Consume(operatorType).Value!;
+
+ AstNode right = builder();
+
+ left = new AstBinaryExpression(@operator, left, right);
+ }
+
+ return left;
+ }
+
+ ///
+ /// EqualityExpression
+ /// : AdditiveExpression
+ /// | AdditiveExpression '=' EqualityExpression
+ /// ;
+ ///
+ private AstNode ParseEqualityExpression()
+ {
+ return ParseBinaryExpression(ParseAdditiveExpression, TokenType.Equals);
+ }
+
+ ///
+ /// AdditiveExpression:
+ /// : MultiplicativeExpression
+ /// | MultiplicativeExpression ADDITIVE_OPERATOR AdditiveExpression
+ /// ;
+ ///
+ private AstNode ParseAdditiveExpression()
+ {
+ return ParseBinaryExpression(ParseMultiplicativeExpression, TokenType.AdditiveOperator);
+ }
+
+ ///
+ /// MultiplicativeExpression:
+ /// : NumericLiteral
+ /// | MultiplicativeExpression MULTIPLICATIVE_OPERATOR NumericLiteral
+ /// ;
+ ///
+ private AstNode ParseMultiplicativeExpression()
+ {
+ return ParseBinaryExpression(ParseNumericLiteral, TokenType.MultiplicativeOperator);
+ }
+
+ ///
+ /// AstNumericLiteral
+ /// : NumberLiteral
+ /// ;
+ ///
+ ///
+ private AstNumericLiteral ParseNumericLiteral()
+ {
+ Token token = Consume(TokenType.NumberLiteral);
+
+ return new AstNumericLiteral((int)token.Value!);
+ }
+
+ private Token Consume(TokenType type)
+ {
+ Token token = _lookahead;
+
+ if (token.Type == TokenType.EndOfFile)
+ throw new Exception($"Unexpected end of input, expected \"{type}\"");
+
+ if (token.Type != type)
+ throw new Exception($"Unexpected token \"{token.Type}\", expected \"{type}\"");
+
+ _lookahead = _tokenizer.GetNextToken();
+
+ return token;
+ }
+}
diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Tokenizer.cs b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Tokenizer.cs
new file mode 100644
index 0000000..8916908
--- /dev/null
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Numberle/Tokenizer.cs
@@ -0,0 +1,116 @@
+namespace GottfriedsNackenWebseite.Numberle;
+
+public enum TokenType
+{
+ Unknown,
+ EndOfFile,
+
+ AdditiveOperator,
+ MultiplicativeOperator,
+ Equals,
+
+ NumberLiteral
+}
+
+public struct Token
+{
+ public TokenType Type;
+ public object? Value;
+}
+
+public class Tokenizer
+{
+ private string _input = string.Empty;
+ private int _cursor = 0;
+
+ public void Init(string input)
+ {
+ _input = input;
+ _cursor = 0;
+ }
+
+ public bool HasMoreTokens => _cursor < _input.Length;
+
+ ///
+ /// Gibt das aktuelle Zeichen der Eingabe zurück oder das Null-Zeichen ('\0') wenn das Ende der Eingabe erreicht wurde.
+ ///
+ private char CurrentChar => HasMoreTokens ? _input[_cursor] : '\0';
+
+ public Token GetNextToken()
+ {
+ if (!HasMoreTokens)
+ {
+ return new Token
+ {
+ Type = TokenType.EndOfFile
+ };
+ }
+
+ Token token = new Token
+ {
+ Type = TokenType.Unknown,
+ Value = null
+ };
+
+ switch (CurrentChar)
+ {
+ case '+':
+ token.Type = TokenType.AdditiveOperator;
+ token.Value = "+";
+ _cursor++;
+ break;
+ case '-':
+ token.Type = TokenType.AdditiveOperator;
+ token.Value = "-";
+ _cursor++;
+ break;
+ case '*':
+ token.Type = TokenType.MultiplicativeOperator;
+ token.Value = "*";
+ _cursor++;
+ break;
+ case '/':
+ token.Type = TokenType.MultiplicativeOperator;
+ token.Value = "/";
+ _cursor++;
+ break;
+ case '=':
+ token.Type = TokenType.Equals;
+ token.Value = "=";
+ _cursor++;
+ break;
+ default:
+ if (char.IsDigit(CurrentChar))
+ {
+ token.Type = TokenType.NumberLiteral;
+
+ int value = 0;
+
+ while (true)
+ {
+ if (!HasMoreTokens)
+ break;
+
+ char c = CurrentChar;
+
+ if (char.IsDigit(c))
+ {
+ value *= 10;
+ value += c - '0';
+
+ _cursor++;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ token.Value = value;
+ }
+ break;
+ }
+
+ return token;
+ }
+}
diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Numberle.razor b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Numberle.razor
new file mode 100644
index 0000000..ee6cbde
--- /dev/null
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Numberle.razor
@@ -0,0 +1,130 @@
+@page "/numberle"
+
+@using GottfriedsNackenWebseite.Components
+@using System.Diagnostics
+@using GottfriedsNackenWebseite.Numberle
+
+Nacken Numberle
+ Gebe eine (korrekte) mathematische Gleichung ein:
+
+
+
+@code
+{
+ private IMask _inputMask = new RegexMask(@"^[0-9+\-*/=]{0,8}$");
+
+ private Parser _parser = new();
+
+ protected override async Task OnInitializedAsync()
+ {
+ await base.OnInitializedAsync();
+ }
+
+ private bool CheckInput(string input)
+ {
+ try
+ {
+ AstNode ast = _parser.Parse(input);
+
+ // Äußerste Node muss '=' sein.
+ if (ast is not AstBinaryExpression binExp || binExp.Operator != "=")
+ return false;
+
+ (decimal value, int equalSigns) = CrunchyValidaty(ast);
+
+ // Wir akzeptieren nur, wenn es exakt ein Gleichheitszeichen gibt und diese erfüllt ist.
+ return value == 1 && equalSigns == 1;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Berechnet den Wert der gegebenen (Teil-)Gleichung und zählt wie viele Gleichheitszeichen darin vorkommen.
+ ///
+ /// Die zu berechnende Gleichung.
+ /// Den Wert der Gleichung und die Anzahl Gleichheitszeichen
+ private (decimal Value, int equalSigns) CrunchyValidaty(AstNode node)
+ {
+ switch (node)
+ {
+ case AstNumericLiteral number:
+ return (number.Value, 0);
+ case AstBinaryExpression binary:
+
+ (decimal Value, int equalSigns) left = CrunchyValidaty(binary.Left);
+ (decimal Value, int equalSigns) right = CrunchyValidaty(binary.Right);
+
+ int equalsSigns = left.equalSigns + right.equalSigns;
+
+ switch (binary.Operator)
+ {
+ case "+":
+ return (left.Value + right.Value, equalsSigns);
+ case "-":
+ return (left.Value - right.Value, equalsSigns);
+ case "*":
+ return (left.Value * right.Value, equalsSigns);
+ case "/":
+ return (left.Value / right.Value, equalsSigns);
+ case "=":
+ return (left.Value == right.Value ? 1 : 0, equalsSigns + 1);
+ }
+ break;
+ default:
+ return (0, 0);
+ }
+
+ return (0, 0);
+ }
+
+ private static readonly char[] Chars = {
+ '=', '+', '-', '*', '/',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
+ };
+
+ private string GenerateSecret()
+ {
+ int charCount = 8;
+
+ char[] formula = new char[charCount];
+
+ // Gleichzeichen plazieren
+ int equalsIndex = Random.Shared.Next(1, charCount - 1);
+ formula[equalsIndex] = '=';
+
+ void PlaceRandomChar(int index)
+ {
+ char left = (index > 0) ? formula[index - 1] : '\0';
+ char right = (index < charCount - 1) ? formula[index + 1] : '\0';
+
+ bool canPlaceOperator = char.IsDigit(left) && char.IsDigit(right);
+
+ if (canPlaceOperator)
+ formula[index] = Chars[Random.Shared.Next(1, Chars.Length)];
+ else
+ formula[index] = Chars[Random.Shared.Next(6, Chars.Length)];
+ }
+
+ // ROHE GEWALT!
+ while (true)
+ {
+ for (int i = 0; i < formula.Length; i++)
+ {
+ if (i == equalsIndex)
+ i++;
+
+ PlaceRandomChar(i);
+
+ string str = new string(formula);
+
+ Debug.WriteLine(str);
+
+ if (CheckInput(str))
+ return str;
+ }
+ }
+ }
+}
diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Wordle.razor b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Wordle.razor
index 2a5145a..84a2d1d 100644
--- a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Wordle.razor
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Pages/Wordle.razor
@@ -25,28 +25,28 @@
@foreach(var guess in PreviousGuesses)
{
-
-
@for(int i = 0; i < 5; i++)
{
-
- var j = i;
- if(@guess.ToUpper()[j] == secret.ToUpper()[j])
+ char guessedChar = char.ToUpper(guess[i]);
+
+ if(guessedChar == char.ToUpper(secret[i]))
{
- @guess.ToUpper()[j]
+ @guessedChar
- }else if (secret.ToUpper().Contains(@guess.ToUpper()[j]))
+ }
+ else if (secret.Contains(guessedChar, StringComparison.InvariantCultureIgnoreCase))
{
- @guess.ToUpper()[j]
+ @guessedChar
- }else
+ }
+ else
{
- @guess.ToUpper()[j]
+ @guessedChar
- }
+ }
}
}
@@ -58,7 +58,7 @@
- Hi
+ Hi
OK
@@ -78,7 +78,10 @@
public List SecretList = new List();
//Keine Ahnung wie das funktionert... Danke StackOverflow :)
- public IMask mask = new RegexMask(@"^[A-Za-z]{0,5}$");
+ ///
+ /// Eine Regexmaske die alle Strings matched die 0 bis 5 Buchstaben enthalten.
+ ///
+ private IMask _inputMask = new RegexMask(@"^[A-Za-z]{0,5}$");
public string secret = "penis";
private bool found = false;
diff --git a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Shared/NavMenu.razor b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Shared/NavMenu.razor
index abb6e79..c7e004f 100644
--- a/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Shared/NavMenu.razor
+++ b/GottfriedsNackenWebseite/GottfriedsNackenWebseite/Shared/NavMenu.razor
@@ -20,6 +20,9 @@
NackenDex
+
+ Nacken Numberle
+
@*