Compare commits

12 Commits

Author SHA1 Message Date
64d18102be WordleKeyboard und viele andere Improvements 2022-04-20 16:27:55 +02:00
626b31a3ce Start of Numberle/Wordle Tastatur 2022-04-19 22:35:37 +02:00
f51bd2bb44 Start of Nacken Numberle 2022-04-19 17:41:26 +02:00
0d5961566b NackenDex Dummy 2022-04-15 14:56:10 +02:00
8cac6f0f2b Fix für deploy 2022-04-14 00:13:34 +02:00
3e162c84b3 Datenbank mit komischen Nomen von Wikipedia 2022-04-13 22:07:06 +02:00
d218b291aa Nacken Wordle fertig
Jetzt fehlt nur noch eine Datenbank mit wörtern
2022-04-13 18:52:44 +02:00
d9b40a6dcc .gitignore hinzugefügt und temporäre Dateien entfernt 2022-04-13 12:27:20 +02:00
db26149bc4 NackenWordle Design 2022-04-10 16:49:48 +02:00
1cdd8631bb Nacken Wordle Seite hinzugefügt 2022-03-25 21:51:02 +01:00
8e5b62c2ef Added Mudblazor and made website sexy 2022-03-21 22:28:55 +01:00
65857f7de5 Added Blazor project 2022-03-21 21:12:45 +01:00
45 changed files with 2378 additions and 0 deletions

398
.gitignore vendored Normal file
View File

@ -0,0 +1,398 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml

View File

@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md

View File

@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,32 @@
using System.Numerics;
namespace GottfriedsNackenUtility;
/// <summary>
/// Bietet nütliche mathematische Funktionen.
/// </summary>
public static class MathHelper
{
/// <summary>
/// Enumeriert die Zahlen der Fibonacci-Folge.
/// </summary>
/// <remarks>
/// Der Datentyp ist <see cref="BigInteger"/>, da es ein paar Fibonacci-Zahlen gibt die nicht von <see cref="int"/> dargestellt werden können.
/// </remarks>
public static IEnumerable<BigInteger> Fibonacci()
{
BigInteger i = 0;
BigInteger j = 1;
while (true)
{
BigInteger fib = i + j;
i = j;
j = fib;
yield return fib;
}
}
}

View File

@ -0,0 +1,42 @@
using System.Numerics;
namespace GottfriedsNackenUtility;
/// <summary>
/// Hier sind suspiziöse Funktionen versteckt.
/// </summary>
public static class SusHelper
{
/// <summary>
/// Dies ist der ausgeklügelste fake-Fortschrittbalkensalgorithmus den die Welt je erblickt hat.
/// </summary>
/// <remarks>
/// Dieser Task läuft in einer Endlosschleife und MUSS mit Hilfe des Abbruchtokens abgebrochen werden!
/// </remarks>
/// <param name="progressChangedCallback">Das callback das aufgerufen wird, wenn der Fortschritt sich geändert hat.
/// Gibt einen Int zwischen 0 und 100 zurück oder -1, wenn der Fortschrittsbalken auf einen "indefinite" Zustand wechseln soll.</param>
/// <param name="cancellationToken">Ein Abbruchtoken, das beim Warten auf den Abschluss der Aufgabe überwacht werden soll.</param>
public static async Task UpdateDefinitelyNotFakeProgress(Action<int> progressChangedCallback, CancellationToken cancellationToken)
{
try
{
foreach (BigInteger fibonacci in MathHelper.Fibonacci())
{
await Task.Delay(250 + (int)(fibonacci % 420), cancellationToken);
if (cancellationToken.IsCancellationRequested)
break;
if (fibonacci.IsEven && (int)(fibonacci % 42) == 0)
{
progressChangedCallback(-1);
await Task.Delay(1337, cancellationToken);
continue;
}
progressChangedCallback((int)(fibonacci % 69));
}
}
catch (TaskCanceledException) { }
}
}

View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.1.32210.238
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GottfriedsNackenWebseite", "GottfriedsNackenWebseite\GottfriedsNackenWebseite.csproj", "{464DD643-DAD9-4E16-BB24-36F789ED4C15}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GottfriedsNackenUtility", "GottfriedsNackenUtility\GottfriedsNackenUtility.csproj", "{0122762F-9961-4D28-8AA9-0D6AE7630208}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{464DD643-DAD9-4E16-BB24-36F789ED4C15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{464DD643-DAD9-4E16-BB24-36F789ED4C15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{464DD643-DAD9-4E16-BB24-36F789ED4C15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{464DD643-DAD9-4E16-BB24-36F789ED4C15}.Release|Any CPU.Build.0 = Release|Any CPU
{0122762F-9961-4D28-8AA9-0D6AE7630208}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0122762F-9961-4D28-8AA9-0D6AE7630208}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0122762F-9961-4D28-8AA9-0D6AE7630208}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0122762F-9961-4D28-8AA9-0D6AE7630208}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {52B17CBC-6882-4F7D-A9B2-DDC733351D9D}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,12 @@
<Router AppAssembly="@typeof(App).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<PageTitle>Not found</PageTitle>
<LayoutView Layout="@typeof(MainLayout)">
<p role="alert">Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>

View File

@ -0,0 +1,60 @@
<MudContainer MaxWidth="MaxWidth.ExtraSmall">
<MudCard Elevation="4">
<MudCardHeader>
<CardHeaderAvatar>
<MudAvatar Image="images/leibniz_icon.jpg"></MudAvatar>
</CardHeaderAvatar>
<CardHeaderContent>
<MudText>Gottfried Wilhelm Leibniz</MudText>
</CardHeaderContent>
<CardHeaderActions>
<MudAvatar Rounded="true" Image="images/german.jpg"></MudAvatar>
</CardHeaderActions>
</MudCardHeader>
<MudCardMedia Image="images/leibniz.jpg" Height="250">
</MudCardMedia>
<MudCardContent>
<MudSimpleTable Style="overflow-x: auto;">
<tbody>
<tr>
<td>Intelligenzquotient</td>
<td>149</td>
</tr>
<tr>
<td>Wissenschaftliche Arbeiten</td>
<td>23</td>
</tr>
<tr>
<td>Alter</td>
<td>70</td>
</tr>
<tr>
<td>Google-Ergebnisse</td>
<td>6.760.000</td>
</tr>
<tr>
<td>Nackengröße</td>
<td>69</td>
</tr>
</tbody>
</MudSimpleTable>
<MudText Class="mt-1">
Gottfried Wilhelm Leibniz (* 21. Juni / 1. Juli 1646 in Leipzig; † 14. November 1716 in Hannover) war ein
deutscher Philosoph, Mathematiker, Jurist, Historiker und politischer Berater der frühen Aufklärung. Er gilt
als der universale Geist seiner Zeit und war einer der bedeutendsten Philosophen des ausgehenden 17. und
beginnenden 18. Jahrhunderts sowie einer der wichtigsten Vordenker der Aufklärung.
</MudText>
</MudCardContent>
<MudCardActions>
<MudIconButton Icon="@Icons.Material.Filled.Favorite" Color="Color.Default" />
<MudText>1337</MudText>
<MudIconButton Class="ml-2" Icon="@Icons.Material.Filled.Share" Color="Color.Default" />
</MudCardActions>
</MudCard>
</MudContainer>
@code {
}

View File

@ -0,0 +1,235 @@
@using System.Diagnostics
<!-- Hidden Alerts -->
<div hidden="@(_alert == AlertState.None)">
<MudContainer MaxWidth="MaxWidth.Small">
<MudGrid>
<MudItem xs="1"></MudItem>
<MudItem xs="8">
@if (_alert == AlertState.Correct)
{
<MudAlert Severity="Severity.Success">Korrekt!</MudAlert>
}
else if (_alert == AlertState.Invalid)
{
<MudAlert Severity="Severity.Error">Eingabe ungültig!</MudAlert>
}
</MudItem>
<MudItem xs="2">
@if (_alert == AlertState.Correct)
{
<MudIconButton Icon="@Icons.Filled.Replay" Color="Color.Primary" aria-label="nochmal spielen" OnClick="@PlayAgain" />
}
</MudItem>
<MudItem xs="1"></MudItem>
</MudGrid>
</MudContainer>
</div>
<!-- Matrix mit Buchstabenfeldern -->
<div class="d-flex justify-center mt-2 mb-2">
<div>
@foreach (CharData[] guess in _previousGuesses)
{
<div class="d-flex mb-2">
@foreach (CharData cd in guess)
{
if (cd.State == CharState.Correct)
{
<MudPaper Style="border-color: green; border-width: 4px;" Class="mr-2" Width="60px" Height="60px" Outlined="true">
<MudText Typo="Typo.h3" Align="Align.Center">@cd.Char</MudText>
</MudPaper>
}
else if (cd.State == CharState.Misplaced)
{
<MudPaper Style="border-color: yellow; border-width: 4px;" Class="mr-2" Width="60px" Height="60px" Outlined="true">
<MudText Typo="Typo.h3" Align="Align.Center">@cd.Char</MudText>
</MudPaper>
}
else
{
<MudPaper Style="border-width: 4px;" Class="mr-2" Width="60px" Height="60px" Outlined="true">
<MudText Typo="Typo.h3" Align="Align.Center">@cd.Char</MudText>
</MudPaper>
}
}
</div>
}
</div>
</div>
@*
<MudTextField T="string" FullWidth="true" Immediate="true" @bind-Value="Input" Label="Bla" Variant="Variant.Outlined" MaxLength="ColumnCount" Counter="ColumnCount" />
<MudTextField T="string" FullWidth="true" Immediate="true" @bind-Value="Input" Label="Blub" Variant="Variant.Outlined" MaxLength="ColumnCount" Counter="ColumnCount" />
*@
<!-- Input dialogue feld -->
<MudContainer MaxWidth="MaxWidth.Small">
<MudGrid Spacing="2">
<MudItem xs="1" />
<MudItem xs="8">
<MudTextField @ref="_textField" T="string" FullWidth="true" Immediate="true" @bind-Value="Input" Label="Dein Tipp:" Variant="Variant.Outlined" MaxLength="ColumnCount" Mask="@InputMask" Counter="ColumnCount" OnKeyDown="OnKeyDown" />
@*<input Type="text" value="@Input"/>*@
</MudItem>
<MudItem xs="2">
<MudButton style="height: 56px; margin-top: 6px;" Class="justify-center" Disabled="Input.Length != ColumnCount"
Size="Size.Large" Variant="Variant.Outlined" EndIcon="@Icons.Material.Filled.Send" Color="Color.Primary" OnClick=@OnEnterGuessClick>OK</MudButton>
</MudItem>
<MudItem xs="1" />
</MudGrid>
</MudContainer>
@code {
private enum AlertState
{
None,
Correct,
Invalid
}
public enum CharState
{
None,
Correct,
Misplaced,
Wrong
}
public struct CharData
{
public char Char;
public CharState State;
public CharData(char c, CharState state)
{
Char = c;
State = state;
}
}
private bool _found = false;
private AlertState _alert = AlertState.None;
private List<CharData[]> _previousGuesses = new();
private MudTextField<string> _textField = default!;
[Parameter]
public string Input { get; set; } = String.Empty;
[Parameter]
public int ColumnCount { get; set; }
public string? Secret { get; set; } = null;
[Parameter]
public IMask InputMask { get; set; } = new RegexMask("^.*$");
/// <summary>
/// Prüft ob die getätigte Eingabe eine mögliche Lösung ist.
/// </summary>
[Parameter]
public Func<string, bool> CheckInput { get; set; } = _ => true;
/// <summary>
/// Wird ausgelöst wenn eine Eingabe getätigt und validiert wurde.
/// </summary>
[Parameter]
public EventCallback<CharData[]> InputValidated { get; set; }
/// <summary>
/// Wird ausgelöst wenn ein neues Secret generiert werden muss.
/// </summary>
[Parameter]
public EventCallback GenerateSecret { get; set; }
public async Task OnKeyDown(KeyboardEventArgs e)
{
_alert = AlertState.None;
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
if(_found)
{
await PlayAgain();
}
else
{
await OnEnterGuessClick();
}
}
}
public async Task OnEnterGuessClick()
{
if (Input.Length != ColumnCount || _found) return;
if (!CheckInput(Input))
{
_alert = AlertState.Invalid;
StateHasChanged();
return;
}
if (Secret == null)
await GenerateSecret.InvokeAsync();
if(await TestInput(Input))
{
_found = true;
_alert = AlertState.Correct;
}
await _textField.Clear();
}
/// <summary>
/// Prüft ob die Eingabe korrekt ist.
/// </summary>
private async Task<bool> TestInput(string input)
{
CharData[] validatedInput = new CharData[ColumnCount];
bool correct = true;
for (int i = 0; i < ColumnCount; i++)
{
char guessedChar = char.ToUpper(input[i]);
CharState state;
if (guessedChar == char.ToUpper(Secret![i]))
{
state = CharState.Correct;
}
else if (Secret.Contains(guessedChar, StringComparison.InvariantCultureIgnoreCase))
{
state = CharState.Misplaced;
correct = false;
}
else
{
state = CharState.Wrong;
correct = false;
}
validatedInput[i] = new CharData(guessedChar, state);
}
_previousGuesses.Add(validatedInput);
await InputValidated.InvokeAsync(validatedInput);
return correct;
}
public async Task PlayAgain()
{
_previousGuesses.Clear();
_alert = AlertState.None;
_found = false;
await GenerateSecret.InvokeAsync();
}
}

View File

@ -0,0 +1,101 @@
<!-- Matrix mit Buchstabenfeldern -->
<div class="d-flex justify-center mt-2 mb-2">
<div>
@{
int index = 0;
for (int row = 0; row < Math.Ceiling((double)Keys.Length / KeysPerRow); row++)
{
<div class="d-flex mb-2">
@for (int i = 0; i < KeysPerRow && index < Keys.Length; i++, index++)
{
char key = Keys[index];
char upperKey = char.ToUpper(key);
_knownKeyStates.TryGetValue(upperKey, out var keyState);
if (keyState == WordleComponent.CharState.Correct)
{
<MudButton Variant="Variant.Outlined" Class="mr-2" Style="width: 5em; height: 5em" Color="Color.Success">
<MudText Typo="Typo.h5">@upperKey</MudText>
</MudButton>
}
else if (keyState == WordleComponent.CharState.Misplaced)
{
<MudButton Variant="Variant.Outlined" Class="mr-2" Style="width: 5em; height: 5em" Color="Color.Warning">
<MudText Typo="Typo.h5">@upperKey</MudText>
</MudButton>
}
else if (keyState == WordleComponent.CharState.Wrong)
{
<MudButton Disabled="true" Variant="Variant.Outlined" Class="mr-2" Style="width: 5em; height: 5em" Color="Color.Error">
<MudText Typo="Typo.h5">@upperKey</MudText>
</MudButton>
}
else
{
<MudButton Variant="Variant.Outlined" Class="mr-2" Style="width: 5em; height: 5em">
<MudText Typo="Typo.h5">@upperKey</MudText>
</MudButton>
}
}
</div>
}
}
</div>
</div>
@code {
[Parameter]
public char[] Keys { get; set; } = Array.Empty<char>();
private Dictionary<char, WordleComponent.CharState> _knownKeyStates = new();
[Parameter]
public int KeysPerRow { get; set; } = 10;
[Parameter]
public string Secret { get; set; } = string.Empty;
[Parameter]
public EventCallback<char> KeyPressed { get; set; }
private async Task ButtonPressed(char c)
{
await KeyPressed.InvokeAsync(c);
}
public void UpdateKeyInfo(WordleComponent.CharData cd)
{
_knownKeyStates.TryGetValue(cd.Char, out var oldState);
WordleComponent.CharState newState = WordleComponent.CharState.None;
switch (cd.State)
{
case WordleComponent.CharState.Correct:
newState = WordleComponent.CharState.Correct;
break;
case WordleComponent.CharState.Misplaced:
// Den besseren Zustand nicht durch schlechteren ersetzen
if (oldState != WordleComponent.CharState.Correct)
{
newState = WordleComponent.CharState.Misplaced;
}
break;
case WordleComponent.CharState.Wrong:
newState = WordleComponent.CharState.Wrong;
break;
}
_knownKeyStates[cd.Char] = newState;
}
public void ResetKeyInfo()
{
_knownKeyStates.Clear();
StateHasChanged();
}
}

View File

@ -0,0 +1,12 @@
namespace GottfriedsNackenWebseite.Data;
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string? Summary { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace GottfriedsNackenWebseite.Data;
public class WeatherForecastService
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate)
{
return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = startDate.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
}).ToArray());
}
}

View File

@ -0,0 +1,22 @@
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
WORKDIR /src
COPY ["GottfriedsNackenWebseite/GottfriedsNackenWebseite.csproj", "GottfriedsNackenWebseite/"]
RUN dotnet restore "GottfriedsNackenWebseite/GottfriedsNackenWebseite.csproj"
COPY . .
WORKDIR "/src/GottfriedsNackenWebseite"
RUN dotnet build "GottfriedsNackenWebseite.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "GottfriedsNackenWebseite.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "GottfriedsNackenWebseite.dll"]

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<UserSecretsId>c5e2b492-e35a-40f7-a290-9076aa64b35e</UserSecretsId>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<None Include="wwwroot\nomen.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.15.0" />
<PackageReference Include="MudBlazor" Version="6.0.7" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\GottfriedsNackenUtility\GottfriedsNackenUtility.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>GottfriedsNackenWebseite</ActiveDebugProfile>
<RazorPage_SelectedScaffolderID>RazorPageScaffolder</RazorPage_SelectedScaffolderID>
<RazorPage_SelectedScaffolderCategoryPath>root/Common/RazorPage</RazorPage_SelectedScaffolderCategoryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebuggerFlavor>ProjectDebugger</DebuggerFlavor>
</PropertyGroup>
</Project>

View File

@ -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;
}
}

View File

@ -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<AstNode> 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;
}
/// <summary>
/// EqualityExpression
/// : AdditiveExpression
/// | AdditiveExpression '=' EqualityExpression
/// ;
/// </summary>
private AstNode ParseEqualityExpression()
{
return ParseBinaryExpression(ParseAdditiveExpression, TokenType.Equals);
}
/// <summary>
/// AdditiveExpression:
/// : MultiplicativeExpression
/// | MultiplicativeExpression ADDITIVE_OPERATOR AdditiveExpression
/// ;
/// </summary>
private AstNode ParseAdditiveExpression()
{
return ParseBinaryExpression(ParseMultiplicativeExpression, TokenType.AdditiveOperator);
}
/// <summary>
/// MultiplicativeExpression:
/// : NumericLiteral
/// | MultiplicativeExpression MULTIPLICATIVE_OPERATOR NumericLiteral
/// ;
/// </summary>
private AstNode ParseMultiplicativeExpression()
{
return ParseBinaryExpression(ParseNumericLiteral, TokenType.MultiplicativeOperator);
}
/// <summary>
/// AstNumericLiteral
/// : NumberLiteral
/// ;
/// </summary>
/// <returns></returns>
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;
}
}

View File

@ -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;
/// <summary>
/// Gibt das aktuelle Zeichen der Eingabe zurück oder das Null-Zeichen ('\0') wenn das Ende der Eingabe erreicht wurde.
/// </summary>
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;
}
}

View File

@ -0,0 +1,18 @@
@page "/counter"
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}

View File

@ -0,0 +1,42 @@
@page
@model GottfriedsNackenWebseite.Pages.ErrorModel
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Error</title>
<link href="~/css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="~/css/site.css" rel="stylesheet" asp-append-version="true" />
</head>
<body>
<div class="main">
<div class="content px-4">
<h1 class="text-danger">Error.</h1>
<h2 class="text-danger">An error occurred while processing your request.</h2>
@if (Model.ShowRequestId)
{
<p>
<strong>Request ID:</strong> <code>@Model.RequestId</code>
</p>
}
<h3>Development Mode</h3>
<p>
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred.
</p>
<p>
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
It can result in displaying sensitive information from exceptions to end users.
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
and restarting the app.
</p>
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,25 @@
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Diagnostics;
namespace GottfriedsNackenWebseite.Pages;
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
[IgnoreAntiforgeryToken]
public class ErrorModel : PageModel
{
public string? RequestId { get; set; }
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
private readonly ILogger<ErrorModel> _logger;
public ErrorModel(ILogger<ErrorModel> logger)
{
_logger = logger;
}
public void OnGet()
{
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier;
}
}

View File

@ -0,0 +1,48 @@
@page "/fetchdata"
<PageTitle>Weather forecast</PageTitle>
@using GottfriedsNackenWebseite.Data
@inject WeatherForecastService ForecastService
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[]? forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
}
}

View File

@ -0,0 +1,68 @@
@page "/"
<PageTitle>Gottfrieds Nackenbox</PageTitle>
<MudContainer MaxWidth="MaxWidth.Medium">
<MudText Typo="Typo.h1">Hallo Welt!</MudText>
<MudText Typo="Typo.h2">Ich bin Gottfried Wilhelm Leibniz!</MudText>
<MudText Typo="Typo.body1">
Und das hier ist meine Webseite.
Sie gefällt bereits <MudText Typo="Typo.body1" Color="@Color.Secondary" Inline=true><b>@_likes</b></MudText> Leuten, gefällt sie dir auch?
</MudText>
<MudText Typo="Typo.body1">
<MudButton Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" StartIcon="@Icons.Material.Filled.Check" OnClick=IncrementLikes>Ja</MudButton>
<MudButton Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" OnClick=IncrementLikes>Absolut</MudButton>
<MudButton Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" OnClick=IncrementLikes>Natürlich</MudButton>
<MudButton Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" OnClick=IncrementLikes>Offensichtlich</MudButton>
<MudButton Variant="Variant.Filled" Size="Size.Large" Color="Color.Primary" OnClick=IncrementLikes>Sicherlich</MudButton>
<MudButton Variant="Variant.Filled" Size="Size.Small" Color="Color.Primary" StartIcon="@Icons.Custom.Uncategorized.Radioactive" Disabled=true>Nein</MudButton>
</MudText>
<MudText Typo="Typo.body1" style="margin-top: 5em;">
Wenn du von dieser Webseite genau so beeindruck bist wie ich, dann sieh dir den Quellcode an:
<MudButton Link="https://gitfrieds.nackenbox.xyz/Gottfried/GottfriedsNackenWebseite"
Target="_blank"
Variant="Variant.Outlined"
EndIcon="@giteaIcon"
Color="Color.Tertiary">
Gitfried Link
</MudButton>
</MudText>
<MudText Typo="Typo.body1" style="margin-top: 5em;">
Es folgt eine wichtige Durchsage von Meiko der Mango:
</MudText>
<MudContainer MaxWidth="MaxWidth.Small">
<MudCard Outlined="true">
<MudCardContent>
<MudText>Story of the day</MudText>
<MudText Typo="Typo.body2">The quick, brown fox jumps over a lazy dog.</MudText>
</MudCardContent>
<MudCardActions>
<MudButton Variant="Variant.Text" Color="Color.Primary">Learn More</MudButton>
</MudCardActions>
</MudCard>
</MudContainer>
</MudContainer>
@code
{
string giteaIcon = @"<svg xmlns=""http://www.w3.org/2000/svg"" aria-hidden=""true"" role=""img"" width=""1em"" height=""1em"" preserveAspectRatio=""xMidYMid meet"" viewBox=""0 0 24 24""><path fill=""currentColor"" d=""M4.209 4.603c-.247 0-.525.02-.84.088c-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768c1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367c2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068c0 0 2.107-4.471 2.107-8.823c-.042-1.318-.367-1.55-.443-1.627c-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17c-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422l-.529 1.075a.686.686 0 0 0-.614.359a.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527a.687.687 0 0 0 .347.763a.686.686 0 0 0 .867-.206a.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02a.657.657 0 0 0 .271-.137a8.826 8.826 0 0 1 1.016.512a.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569c-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477a.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431c.19-.397.515-1.16.515-1.16c.035-.066.218-.394.103-.814c-.095-.435-.48-.638-.48-.638c-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062l2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657c-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08l-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z""/></svg>";
/// <summary>
/// Anzahl der Likes die die Webseite bereits bekommen hat.
/// </summary>
static int _likes = 1337;
/// <summary>
/// Inkrementiert <see cref="_likes"/>.
/// </summary>
private void IncrementLikes()
{
_likes++;
}
}

View File

@ -0,0 +1,37 @@
@page "/nackendex"
<PageTitle>NackenDex</PageTitle>
<MudText Class="mt-2 mb-2" Align="Align.Center">Ihr NackenDex für die besten Quartettkarten</MudText>
<MudContainer>
<MudPaper Elevation="25" Class="m-1">
<MudGrid Spacing="2">
<MudItem Class="ml-2">
<MudAutocomplete T="string" Label="Suche..."></MudAutocomplete>
</MudItem>
<MudSpacer />
<MudItem Class="mr-2 mt-2">
<MudChipSet @bind-SelectedChips="selected" Filter="filter">
<MudChip Text="Deutsch"></MudChip>
<MudChip Text="Nacken"></MudChip>
<MudChip Text="Intelligenz"></MudChip>
<MudChip Text="Alter"></MudChip>
</MudChipSet>
</MudItem>
</MudGrid>
</MudPaper>
</MudContainer>
<MudGrid Justify="Justify.Center" Class="mt-8">
@for(int i = 0; i < 10; i++)
{
<MudItem>
<GottfriedsNackenWebseite.Components.NackenKarte></GottfriedsNackenWebseite.Components.NackenKarte>
</MudItem>
}
</MudGrid>
@code {
bool filter = true;
MudChip[] selected;
}

View File

@ -0,0 +1,218 @@
@page "/numberle"
@using GottfriedsNackenWebseite.Components
@using GottfriedsNackenWebseite.Numberle
@using GottfriedsNackenUtility
@using System.Diagnostics
@using System.Numerics
<PageTitle>Nacken Numberle</PageTitle>
<MudText Align="Align.Center" Class="mt-2 mb-2"> Gebe eine (korrekte) mathematische Gleichung ein:</MudText>
<WordleComponent ColumnCount="8" GenerateSecret="OnGenerateNewSecret" InputMask="@_inputMask" CheckInput="CheckInput" InputValidated="OnInputValidated" @ref="_wordle" />
<WordleKeyboardComponent @ref="_keyboard" KeyPressed="KeyboardKeyPressed" Keys="Chars" />
<MudOverlay @bind-Visible="_showLoadingOverlay" DarkBackground="true">
<MudCard>
<MudCardHeader>
<MudText Typo="Typo.h5">Bitte warten...</MudText>
</MudCardHeader>
<MudCardContent>
<MudText>Erzeuge eine unknackbare Formel mit roher Gewalt!</MudText>
<MudProgressLinear Color="Color.Secondary" Indeterminate="_progress < 0" Value="_progress" Class="my-7"/>
</MudCardContent>
</MudCard>
</MudOverlay>
@code
{
private static readonly IMask _inputMask = new RegexMask(@"^[0-9+\-*/=]{0,8}$");
private readonly Parser _parser = new();
private WordleComponent _wordle = default!;
private WordleKeyboardComponent _keyboard = default!;
private bool _showLoadingOverlay;
private int _progress;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
}
/// <summary>
/// Prüft ob input eine gültige Gleichung ist.
/// </summary>
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 die Gleichung erfüllt ist.
return value == 1 && equalSigns == 1;
}
catch
{
return false;
}
}
/// <summary>
/// Berechnet den Wert der gegebenen (Teil-)Gleichung und zählt wie viele Gleichheitszeichen darin vorkommen.
/// </summary>
/// <param name="node">Die zu berechnende Gleichung.</param>
/// <returns>Den Wert der Gleichung und die Anzahl Gleichheitszeichen</returns>
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);
}
/// <summary>
/// Die Zeichen aus denen eine Antwort besteht.
/// </summary>
private static readonly char[] Chars = {
'=', '+', '-', '*', '/',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
/// <summary>
/// Generiert ein neues Geheimnis und zeigt während dessen den Ladebalken an.
/// </summary>
/// <returns></returns>
private async Task OnGenerateNewSecret()
{
_keyboard.ResetKeyInfo();
CancellationTokenSource cts = new CancellationTokenSource();
Task? amazingTask = null;
try
{
// Ladebalkenaktualisierer starten
amazingTask = SusHelper.UpdateDefinitelyNotFakeProgress(progress =>
{
_progress = progress;
StateHasChanged();
}, cts.Token);
_showLoadingOverlay = true;
_wordle.Secret = await Task.Run(GenerateFormula);
_showLoadingOverlay = false;
}
finally
{
// Ladebalkenaktualisierer stoppen
cts.Cancel();
if (amazingTask != null)
await amazingTask;
}
}
/// <summary>
/// Erzeugt eine neue (gültige) Formel.
/// </summary>
/// <returns>Die Formel</returns>
private string GenerateFormula()
{
int charCount = 8;
char[] formula = new char[charCount];
// Gleichzeichen plazieren
int equalsIndex = Random.Shared.Next(1, charCount - 1);
formula[equalsIndex] = '=';
// Ersetzt das Zeichen mit Index "index" durch einen Zufälligen wert.
// TODO: ein etwas gewiefterer Algorithmus würde nicht schaden.
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;
}
}
}
}
private async Task KeyboardKeyPressed(char key)
{
// TODO: Virtuelles Keyboard
//await _wordle.SetText(_wordle.Input + key); //Input += key;
await Task.CompletedTask;
}
private void OnInputValidated(WordleComponent.CharData[] data)
{
// Auswertung in das Keyboard packen
foreach (WordleComponent.CharData cd in data)
{
_keyboard.UpdateKeyInfo(cd);
}
}
}

View File

@ -0,0 +1,155 @@
@page "/wordle"
<PageTitle>Nacken Wordle</PageTitle>
<MudText Align="Align.Center" Class="mt-2 mb-2"> Rate ein Wort mit 5 Buchstaben:</MudText>
<!-- Hidden Alerts -->
<div hidden="@hideAlert">
<MudContainer MaxWidth="MaxWidth.Small">
<MudGrid>
<MudItem xs="1"></MudItem>
<MudItem xs="8">
<MudAlert Severity="Severity.Success">Korrekt!</MudAlert>
</MudItem>
<MudItem xs="2">
<MudIconButton Icon="@Icons.Filled.Replay" Color="Color.Primary" aria-label="nochmal spielen" OnClick="@PlayAgain"></MudIconButton>
</MudItem>
<MudItem xs="1"></MudItem>
</MudGrid>
</MudContainer>
</div>
<!-- Matrix mit Buchstabenfeldern -->
<div class="d-flex justify-center mt-2 mb-2">
<div>
@foreach(var guess in PreviousGuesses)
{
<div class="d-flex mb-2">
@for(int i = 0; i < 5; i++)
{
char guessedChar = char.ToUpper(guess[i]);
if(guessedChar == char.ToUpper(secret[i]))
{
<MudPaper Style="border-color: green; border-width: 4px;" Class="mr-2" Width="60px" Height="60px" Outlined="true">
<MudText Typo="Typo.h3" Align="Align.Center">@guessedChar</MudText>
</MudPaper>
}
else if (secret.Contains(guessedChar, StringComparison.InvariantCultureIgnoreCase))
{
<MudPaper Style="border-color: yellow; border-width: 4px;" Class="mr-2" Width="60px" Height="60px" Outlined="true">
<MudText Typo="Typo.h3" Align="Align.Center">@guessedChar</MudText>
</MudPaper>
}
else
{
<MudPaper Style="border-width: 4px;" Class="mr-2" Width="60px" Height="60px" Outlined="true">
<MudText Typo="Typo.h3" Align="Align.Center">@guessedChar</MudText>
</MudPaper>
}
}
</div>
}
</div>
</div>
<!-- Input dialogue feld -->
<MudContainer MaxWidth="MaxWidth.Small" @onkeyup="@Check4Enter">
<MudGrid Spacing="2">
<MudItem xs="1"></MudItem>
<MudItem xs="8">
<MudTextField Class="" @ref="textField" T="string" FullWidth="true" @bind-Value="Input" Label="Dein Tipp:" Variant="Variant.Outlined" MaxLength="5" Mask="@_inputMask">Hi</MudTextField>
</MudItem>
<MudItem xs="2">
<MudButton style="height:56px; margin-top:6px;" Class="justify-center" Size="Size.Large" Variant="Variant.Outlined" EndIcon="@Icons.Material.Filled.Send" Color="Color.Primary" OnClick=@ButtonOnClick>OK</MudButton>
</MudItem>
<MudItem xs="1"></MudItem>
</MudGrid>
</MudContainer>
@code {
public string Input { get; set; }
public bool hideAlert = true;
public MudTextField<string> textField;
public List<string> PreviousGuesses = new List<string>();
public List<string> SecretList = new List<string>();
//Keine Ahnung wie das funktionert... Danke StackOverflow :)
/// <summary>
/// Eine Regexmaske die alle Strings matched die 0 bis 5 Buchstaben enthalten.
/// </summary>
private IMask _inputMask = new RegexMask(@"^[A-Za-z]{0,5}$");
public string secret = "penis";
private bool found = false;
protected override void OnInitialized()
{
try
{
string text = File.ReadAllText("./wwwroot/nomen.txt");
string[] splitArray = text.Split(",");
foreach(var w in splitArray)
{
SecretList.Add(w);
}
Random r = new Random();
secret = SecretList.ElementAt<string>(r.Next(SecretList.Count));
Console.WriteLine("[SECRET]: " + secret.ToLower());
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
public void Check4Enter(KeyboardEventArgs e)
{
if (e.Code == "Enter" || e.Code == "NumpadEnter")
{
if(found)
{
PlayAgain();
}
else
{
ButtonOnClick();
}
}
}
public void ButtonOnClick()
{
if(Input != null && Input.Length == 5 && !found)
{
PreviousGuesses.Add(Input);
if(secret.ToLower() == Input.ToLower())
{
found = true;
textField.Clear();
hideAlert = false;
}
else
{
textField.Clear();
}
}
}
public void PlayAgain()
{
PreviousGuesses.Clear();
hideAlert = true;
found = false;
Random r = new Random();
secret = SecretList.ElementAt<string>(r.Next(SecretList.Count));
Console.WriteLine("[SECRET]: " + secret.ToLower());
}
}

View File

@ -0,0 +1,8 @@
@page "/"
@namespace GottfriedsNackenWebseite.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@{
Layout = "_Layout";
}
<component type="typeof(App)" render-mode="ServerPrerendered" />

View File

@ -0,0 +1,37 @@
@using Microsoft.AspNetCore.Components.Web
@namespace GottfriedsNackenWebseite.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="~/" />
<link href="css/site.css" rel="stylesheet" />
<link href="GottfriedsNackenWebseite.styles.css" rel="stylesheet" />
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />
@* Mudblazor *@
<link href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" rel="stylesheet" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
</head>
<body>
@RenderBody()
<div id="blazor-error-ui">
<environment include="Staging,Production">
An error has occurred. This application may no longer respond until reloaded.
</environment>
<environment include="Development">
An unhandled exception has occurred. See browser dev tools for details.
</environment>
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.server.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,33 @@
using GottfriedsNackenWebseite.Data;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using MudBlazor.Services;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor();
builder.Services.AddSingleton<WeatherForecastService>();
builder.Services.AddMudServices();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();

View File

@ -0,0 +1,35 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:2865",
"sslPort": 44302
}
},
"profiles": {
"GottfriedsNackenWebseite": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:7008;http://localhost:5008",
"dotnetRunMessages": true
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true,
"useSSL": true
}
}
}

View File

@ -0,0 +1,62 @@
@inherits LayoutComponentBase
<MudThemeProvider @ref=_mudThemeProvider IsDarkMode=_isDarkMode Theme=_theme/>
<MudDialogProvider/>
<MudSnackbarProvider/>
<MudLayout>
<MudAppBar>
<MudIconButton Icon="@Icons.Material.Filled.Menu" Color=Color.Inherit Edge=Edge.Start OnClick="@((e) => DrawerToggle())"></MudIconButton>
Gottfrieds Nackenbox
<MudSwitch @bind-Checked="@_isDarkMode" Color="Color.Primary" Class="ma-4" T="bool" Label="Toggle Light/Dark Mode"/>
</MudAppBar>
<MudDrawer @bind-Open=_drawerOpen>
<NavMenu />
</MudDrawer>
<MudMainContent>
@Body
</MudMainContent>
</MudLayout>
@code
{
private MudTheme _theme = new();
//{
// Palette = new Palette()
// {
// Primary = Colors.Green.Darken1,
// Secondary = Colors.Orange.Darken4,
// AppbarBackground = Colors.Green.Darken1,
// },
// PaletteDark = new Palette()
// {
// Primary = Colors.Blue.Lighten1
// },
// LayoutProperties = new LayoutProperties()
// {
// DrawerWidthLeft = "260px",
// DrawerWidthRight = "300px"
// }
//};
private MudThemeProvider? _mudThemeProvider;
bool _isDarkMode = false;
bool _drawerOpen = true;
void DrawerToggle()
{
_drawerOpen = !_drawerOpen;
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_isDarkMode = await _mudThemeProvider!.GetSystemPreference();
StateHasChanged();
}
}
}

View File

@ -0,0 +1,70 @@
.page {
position: relative;
display: flex;
flex-direction: column;
}
main {
flex: 1;
}
.sidebar {
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}
.top-row {
background-color: #f7f7f7;
border-bottom: 1px solid #d6d5d5;
justify-content: flex-end;
height: 3.5rem;
display: flex;
align-items: center;
}
.top-row ::deep a, .top-row .btn-link {
white-space: nowrap;
margin-left: 1.5rem;
}
.top-row a:first-child {
overflow: hidden;
text-overflow: ellipsis;
}
@media (max-width: 640.98px) {
.top-row:not(.auth) {
display: none;
}
.top-row.auth {
justify-content: space-between;
}
.top-row a, .top-row .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row, article {
padding-left: 2rem !important;
padding-right: 1.5rem !important;
}
}

View File

@ -0,0 +1,67 @@
<MudDrawerHeader>
<MudCardHeader>
Gottfrieds Nackenbox
</MudCardHeader>
</MudDrawerHeader>
<MudNavMenu>
<MudNavLink Href="" Match=NavLinkMatch.All>
Home
</MudNavLink>
<MudNavLink Href="counter" Match=NavLinkMatch.Prefix>
Counter
</MudNavLink>
<MudNavLink Href="fetchdata" Match=NavLinkMatch.Prefix>
Fetch data
</MudNavLink>
<MudNavLink Href="wordle" Match=NavLinkMatch.Prefix>
Nacken Wordle
</MudNavLink>
<MudNavLink Href="nackendex" Match=NavLinkMatch.Prefix>
NackenDex
</MudNavLink>
<MudNavLink Href="numberle" Match=NavLinkMatch.Prefix>
Nacken Numberle
</MudNavLink>
</MudNavMenu>
@*
<div class="top-row ps-3 navbar navbar-dark">
<div class="container-fluid">
<a class="navbar-brand" href="">GottfriedsNackenWebseite</a>
<button title="Navigation menu" class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
</div>
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<nav class="flex-column">
<div class="nav-item px-3">
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
<span class="oi oi-home" aria-hidden="true"></span> Home
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="counter">
<span class="oi oi-plus" aria-hidden="true"></span> Counter
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="fetchdata">
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
</NavLink>
</div>
</nav>
</div>
@code {
private bool collapseNavMenu = true;
private string? NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu()
{
collapseNavMenu = !collapseNavMenu;
}
}
*@

View File

@ -0,0 +1,62 @@
.navbar-toggler {
background-color: rgba(255, 255, 255, 0.1);
}
.top-row {
height: 3.5rem;
background-color: rgba(0,0,0,0.4);
}
.navbar-brand {
font-size: 1.1rem;
}
.oi {
width: 2rem;
font-size: 1.1rem;
vertical-align: text-top;
top: -2px;
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item ::deep a {
color: #d7d7d7;
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
}
.nav-item ::deep a.active {
background-color: rgba(255,255,255,0.25);
color: white;
}
.nav-item ::deep a:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
@media (min-width: 641px) {
.navbar-toggler {
display: none;
}
.collapse {
/* Never collapse the sidebar for wide screens */
display: block;
}
}

View File

@ -0,0 +1,16 @@
<div class="alert alert-secondary mt-4">
<span class="oi oi-pencil me-2" aria-hidden="true"></span>
<strong>@Title</strong>
<span class="text-nowrap">
Please take our
<a target="_blank" class="font-weight-bold link-dark" href="https://go.microsoft.com/fwlink/?linkid=2149017">brief survey</a>
</span>
and tell us what you think.
</div>
@code {
// Demonstrates how a parent component can supply parameters
[Parameter]
public string? Title { get; set; }
}

View File

@ -0,0 +1,11 @@
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using GottfriedsNackenWebseite
@using GottfriedsNackenWebseite.Shared
@using MudBlazor

View File

@ -0,0 +1,9 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,9 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,62 @@
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
h1:focus {
outline: none;
}
a, .btn-link {
color: #0071c1;
}
.btn-primary {
color: #fff;
background-color: #1b6ec2;
border-color: #1861ac;
}
.content {
padding-top: 1.1rem;
}
.valid.modified:not([type=checkbox]) {
outline: 1px solid #26b050;
}
.invalid {
outline: 1px solid red;
}
.validation-message {
color: red;
}
#blazor-error-ui {
background: lightyellow;
bottom: 0;
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
display: none;
left: 0;
padding: 0.6rem 1.25rem 0.7rem 1.25rem;
position: fixed;
width: 100%;
z-index: 1000;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
.blazor-error-boundary {
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
padding: 1rem 1rem 1rem 3.7rem;
color: white;
}
.blazor-error-boundary::after {
content: "An error has occurred."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path fill="currentColor" d="M4.209 4.603c-.247 0-.525.02-.84.088c-.333.07-1.28.283-2.054 1.027C-.403 7.25.035 9.685.089 10.052c.065.446.263 1.687 1.21 2.768c1.749 2.141 5.513 2.092 5.513 2.092s.462 1.103 1.168 2.119c.955 1.263 1.936 2.248 2.89 2.367c2.406 0 7.212-.004 7.212-.004s.458.004 1.08-.394c.535-.324 1.013-.893 1.013-.893s.492-.527 1.18-1.73c.21-.37.385-.729.538-1.068c0 0 2.107-4.471 2.107-8.823c-.042-1.318-.367-1.55-.443-1.627c-.156-.156-.366-.153-.366-.153s-4.475.252-6.792.306c-.508.011-1.012.023-1.512.027v4.474l-.634-.301c0-1.39-.004-4.17-.004-4.17c-1.107.016-3.405-.084-3.405-.084s-5.399-.27-5.987-.324c-.187-.011-.401-.032-.648-.032zm.354 1.832h.111s.271 2.269.6 3.597C5.549 11.147 6.22 13 6.22 13s-.996-.119-1.641-.348c-.99-.324-1.409-.714-1.409-.714s-.73-.511-1.096-1.52C1.444 8.73 2.021 7.7 2.021 7.7s.32-.859 1.47-1.145c.395-.106.863-.12 1.072-.12zm8.33 2.554c.26.003.509.127.509.127l.868.422l-.529 1.075a.686.686 0 0 0-.614.359a.685.685 0 0 0 .072.756l-.939 1.924a.69.69 0 0 0-.66.527a.687.687 0 0 0 .347.763a.686.686 0 0 0 .867-.206a.688.688 0 0 0-.069-.882l.916-1.874a.667.667 0 0 0 .237-.02a.657.657 0 0 0 .271-.137a8.826 8.826 0 0 1 1.016.512a.761.761 0 0 1 .286.282c.073.21-.073.569-.073.569c-.087.29-.702 1.55-.702 1.55a.692.692 0 0 0-.676.477a.681.681 0 1 0 1.157-.252c.073-.141.141-.282.214-.431c.19-.397.515-1.16.515-1.16c.035-.066.218-.394.103-.814c-.095-.435-.48-.638-.48-.638c-.467-.301-1.116-.58-1.116-.58s0-.156-.042-.27a.688.688 0 0 0-.148-.241l.516-1.062l2.89 1.401s.48.218.583.619c.073.282-.019.534-.069.657c-.24.587-2.1 4.317-2.1 4.317s-.232.554-.748.588a1.065 1.065 0 0 1-.393-.045l-.202-.08l-4.31-2.1s-.417-.218-.49-.596c-.083-.31.104-.691.104-.691l2.073-4.272s.183-.37.466-.497a.855.855 0 0 1 .35-.077z"/></svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because one or more lines are too long