refactor: reorganize project structure and move examples
- Move examples (Blackjack, TicTacToe) to the root directory - Convert example projects from Console applications to Libraries - Add Directory.Build.props for shared build settings - Update solution file and project references to reflect new paths
This commit is contained in:
parent
5594515a53
commit
7946f4bafd
|
|
@ -0,0 +1,17 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>Blackjack</RootNamespace>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OECS\OECS.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\OECS.SourceGen\OECS.SourceGen.csproj"
|
||||||
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project>
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<LangVersion>12</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -2,9 +2,6 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<LangVersion>12</LangVersion>
|
|
||||||
<RootNamespace>OECS.SourceGen</RootNamespace>
|
<RootNamespace>OECS.SourceGen</RootNamespace>
|
||||||
<AssemblyName>OECS.SourceGen</AssemblyName>
|
<AssemblyName>OECS.SourceGen</AssemblyName>
|
||||||
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<RootNamespace>OECS.Tests</RootNamespace>
|
<RootNamespace>OECS.Tests</RootNamespace>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
|
|
@ -20,11 +17,11 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\OECS\OECS.csproj" />
|
<ProjectReference Include="..\OECS\OECS.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\OECS.SourceGen\OECS.SourceGen.csproj"
|
<ProjectReference Include="..\OECS.SourceGen\OECS.SourceGen.csproj"
|
||||||
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
55
OECS.sln
55
OECS.sln
|
|
@ -1,19 +1,16 @@
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 17
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 17.0.31903.59
|
VisualStudioVersion = 17.0.31903.59
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OECS.SourceGen", "src\OECS.SourceGen\OECS.SourceGen.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OECS", "OECS\OECS.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OECS", "src\OECS\OECS.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OECS.SourceGen", "OECS.SourceGen\OECS.SourceGen.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OECS.Tests", "tests\OECS.Tests\OECS.Tests.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OECS.Tests", "OECS.Tests\OECS.Tests.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blackjack", "Blackjack\Blackjack.csproj", "{85631EDE-F984-4DA6-8EE9-0715AF1204E6}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blackjack", "examples\Blackjack\Blackjack.csproj", "{85631EDE-F984-4DA6-8EE9-0715AF1204E6}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TicTacToe", "TicTacToe\TicTacToe.csproj", "{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}"
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{827E0CD3-B72D-47B6-A68D-7590B98EB39B}"
|
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
|
@ -25,18 +22,6 @@ Global
|
||||||
Release|x86 = Release|x86
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x64.ActiveCfg = Debug|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x64.Build.0 = Debug|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x86.ActiveCfg = Debug|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x86.Build.0 = Debug|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x64.ActiveCfg = Release|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x64.Build.0 = Release|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x86.ActiveCfg = Release|Any CPU
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x86.Build.0 = Release|Any CPU
|
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
|
@ -49,6 +34,18 @@ Global
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
|
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|x86.Build.0 = Release|Any CPU
|
||||||
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|x64.ActiveCfg = Debug|Any CPU
|
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
|
@ -73,12 +70,20 @@ Global
|
||||||
{85631EDE-F984-4DA6-8EE9-0715AF1204E6}.Release|x64.Build.0 = Release|Any CPU
|
{85631EDE-F984-4DA6-8EE9-0715AF1204E6}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{85631EDE-F984-4DA6-8EE9-0715AF1204E6}.Release|x86.ActiveCfg = Release|Any CPU
|
{85631EDE-F984-4DA6-8EE9-0715AF1204E6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{85631EDE-F984-4DA6-8EE9-0715AF1204E6}.Release|x86.Build.0 = Release|Any CPU
|
{85631EDE-F984-4DA6-8EE9-0715AF1204E6}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D4E5F6A7-B8C9-0123-DEF4-567890ABCDEF}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{C3D4E5F6-A7B8-9012-CDEF-123456789012} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B}
|
|
||||||
{85631EDE-F984-4DA6-8EE9-0715AF1204E6} = {B36A84DF-456D-A817-6EDD-3EC3E7F6E11F}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<RootNamespace>OECS</RootNamespace>
|
<RootNamespace>OECS</RootNamespace>
|
||||||
<AssemblyName>OECS</AssemblyName>
|
<AssemblyName>OECS</AssemblyName>
|
||||||
|
|
||||||
|
|
@ -1,19 +1,16 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Library</OutputType>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<RootNamespace>TicTacToe</RootNamespace>
|
<RootNamespace>TicTacToe</RootNamespace>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\OECS\OECS.csproj" />
|
<ProjectReference Include="..\OECS\OECS.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\OECS.SourceGen\OECS.SourceGen.csproj"
|
<ProjectReference Include="..\OECS.SourceGen\OECS.SourceGen.csproj"
|
||||||
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>Exe</OutputType>
|
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<RootNamespace>Blackjack</RootNamespace>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\src\OECS\OECS.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\src\OECS.SourceGen\OECS.SourceGen.csproj"
|
|
||||||
OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
|
||||||
|
|
@ -1,114 +0,0 @@
|
||||||
using OECS;
|
|
||||||
|
|
||||||
namespace Blackjack;
|
|
||||||
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
public static void Main()
|
|
||||||
{
|
|
||||||
var world = new World();
|
|
||||||
|
|
||||||
// ── Initialize game state ─────────────────────────────────────
|
|
||||||
|
|
||||||
// Use a seed based on current time, or a fixed seed for reproducibility.
|
|
||||||
uint seed = (uint)Environment.TickCount;
|
|
||||||
// Uncomment the next line for a deterministic game:
|
|
||||||
// seed = 12345u;
|
|
||||||
|
|
||||||
world.SetSingleton(new GameState
|
|
||||||
{
|
|
||||||
Phase = GamePhase.Betting,
|
|
||||||
Result = RoundResult.None,
|
|
||||||
Chips = 100,
|
|
||||||
CurrentBet = 0,
|
|
||||||
RoundNumber = 1,
|
|
||||||
Seed = seed
|
|
||||||
});
|
|
||||||
world.PostChanges();
|
|
||||||
|
|
||||||
// ── Wire up systems ───────────────────────────────────────────
|
|
||||||
|
|
||||||
var group = new SystemGroup(world);
|
|
||||||
group.Add(new DeckSetupSystem());
|
|
||||||
group.Add(new DealSystem());
|
|
||||||
group.Add(new PlayerBustCheckSystem());
|
|
||||||
group.Add(new DealerSystem());
|
|
||||||
group.Add(new RenderSystem());
|
|
||||||
|
|
||||||
// ── Game loop ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
group.RunLogical();
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
var state = world.ReadSingleton<GameState>();
|
|
||||||
|
|
||||||
// Check for game over (out of chips).
|
|
||||||
if (state.Chips <= 0 && state.Phase == GamePhase.Betting)
|
|
||||||
{
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine(" You're out of chips! Game over.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (state.Phase)
|
|
||||||
{
|
|
||||||
case GamePhase.Betting:
|
|
||||||
var input = Console.ReadLine();
|
|
||||||
if (string.IsNullOrWhiteSpace(input))
|
|
||||||
continue;
|
|
||||||
if (input.Trim().ToLower() == "quit")
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (int.TryParse(input.Trim(), out var bet))
|
|
||||||
{
|
|
||||||
world.Commands.Enqueue(new PlaceBetCommand { Amount = bet });
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Console.WriteLine(" Invalid bet. Enter a number.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GamePhase.PlayerTurn:
|
|
||||||
var choice = Console.ReadLine();
|
|
||||||
if (string.IsNullOrWhiteSpace(choice))
|
|
||||||
continue;
|
|
||||||
if (choice.Trim().ToLower() == "quit")
|
|
||||||
return;
|
|
||||||
|
|
||||||
switch (choice.Trim().ToLower())
|
|
||||||
{
|
|
||||||
case "h":
|
|
||||||
case "hit":
|
|
||||||
world.Commands.Enqueue(new HitCommand());
|
|
||||||
break;
|
|
||||||
case "s":
|
|
||||||
case "stand":
|
|
||||||
world.Commands.Enqueue(new StandCommand());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Console.WriteLine(" Invalid choice. Enter H or S.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case GamePhase.RoundOver:
|
|
||||||
Console.ReadLine();
|
|
||||||
ref var roundState = ref world.GetSingleton<GameState>();
|
|
||||||
roundState.RoundNumber++;
|
|
||||||
world.MarkModified<GameState>(World.SingletonEntity);
|
|
||||||
world.PostChanges();
|
|
||||||
world.Commands.Enqueue(new NewRoundCommand());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Dealing, DealerTurn — systems handle these automatically.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
group.RunLogical();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,148 +0,0 @@
|
||||||
using OECS;
|
|
||||||
|
|
||||||
namespace Blackjack;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Renders the current game state to the console.
|
|
||||||
/// </summary>
|
|
||||||
public class RenderSystem : ISystem
|
|
||||||
{
|
|
||||||
public void Run(World world)
|
|
||||||
{
|
|
||||||
var state = world.ReadSingleton<GameState>();
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine(" Blackjack");
|
|
||||||
Console.WriteLine(" ═════════");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine($" Chips: {state.Chips} | Round: {state.RoundNumber}");
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
// Show dealer's hand.
|
|
||||||
var dealerTotal = HandUtil.CalculateHand(world, DealerHandTag.Instance);
|
|
||||||
Console.Write(" Dealer: ");
|
|
||||||
if (state.Phase == GamePhase.PlayerTurn)
|
|
||||||
{
|
|
||||||
// Hide first card (hole card) during player's turn.
|
|
||||||
Console.Write("?? ");
|
|
||||||
PrintHand(world, DealerHandTag.Instance, skipFirst: true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PrintHand(world, DealerHandTag.Instance);
|
|
||||||
Console.Write($" ({dealerTotal})");
|
|
||||||
}
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
// Show player's hand.
|
|
||||||
var playerTotal = HandUtil.CalculateHand(world, PlayerHandTag.Instance);
|
|
||||||
Console.Write(" Player: ");
|
|
||||||
PrintHand(world, PlayerHandTag.Instance);
|
|
||||||
Console.Write($" ({playerTotal})");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
// Status line.
|
|
||||||
switch (state.Phase)
|
|
||||||
{
|
|
||||||
case GamePhase.Betting:
|
|
||||||
Console.Write($" Enter bet (1–{state.Chips}): ");
|
|
||||||
break;
|
|
||||||
case GamePhase.PlayerTurn:
|
|
||||||
Console.Write(" [H]it or [S]tand: ");
|
|
||||||
break;
|
|
||||||
case GamePhase.RoundOver:
|
|
||||||
Console.WriteLine($" {ResultText(state.Result)}");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.Write(" Press Enter for next round...");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintHand(World world, DealerHand hand, bool skipFirst = false)
|
|
||||||
{
|
|
||||||
PrintHandImpl(world, hand, skipFirst);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintHand(World world, PlayerHand hand)
|
|
||||||
{
|
|
||||||
PrintHandImpl(world, hand, skipFirst: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintHandImpl<T>(World world, T handTag, bool skipFirst)
|
|
||||||
where T : struct
|
|
||||||
{
|
|
||||||
Entity handEntity = Entity.Null;
|
|
||||||
using (var iter = world.Select<T>())
|
|
||||||
{
|
|
||||||
while (iter.MoveNext())
|
|
||||||
{
|
|
||||||
if (iter.CurrentEntity != World.SingletonEntity)
|
|
||||||
{
|
|
||||||
handEntity = iter.CurrentEntity;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handEntity == Entity.Null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var cards = world.GetSources<Holds>(handEntity);
|
|
||||||
var ordered = cards.OrderByDescending(c =>
|
|
||||||
{
|
|
||||||
var card = world.ReadComponent<Card>(c);
|
|
||||||
return (int)card.Rank;
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
bool first = true;
|
|
||||||
foreach (var cardEntity in ordered)
|
|
||||||
{
|
|
||||||
if (skipFirst && first)
|
|
||||||
{
|
|
||||||
first = false;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
var card = world.ReadComponent<Card>(cardEntity);
|
|
||||||
Console.Write(CardToString(card));
|
|
||||||
Console.Write(" ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string CardToString(Card card)
|
|
||||||
{
|
|
||||||
var suit = card.Suit switch
|
|
||||||
{
|
|
||||||
Suit.Hearts => "♥",
|
|
||||||
Suit.Diamonds => "♦",
|
|
||||||
Suit.Clubs => "♣",
|
|
||||||
Suit.Spades => "♠",
|
|
||||||
_ => "?"
|
|
||||||
};
|
|
||||||
|
|
||||||
var rank = card.Rank switch
|
|
||||||
{
|
|
||||||
Rank.Ace => "A",
|
|
||||||
Rank.Jack => "J",
|
|
||||||
Rank.Queen => "Q",
|
|
||||||
Rank.King => "K",
|
|
||||||
_ => ((int)card.Rank).ToString()
|
|
||||||
};
|
|
||||||
|
|
||||||
return $"{rank}{suit}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ResultText(RoundResult result) => result switch
|
|
||||||
{
|
|
||||||
RoundResult.PlayerBust => "You bust! Dealer wins.",
|
|
||||||
RoundResult.DealerBust => "Dealer busts! You win!",
|
|
||||||
RoundResult.PlayerWin => "You win!",
|
|
||||||
RoundResult.DealerWin => "Dealer wins.",
|
|
||||||
RoundResult.Push => "Push — it's a tie!",
|
|
||||||
RoundResult.Blackjack => "Blackjack!",
|
|
||||||
_ => ""
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,153 +0,0 @@
|
||||||
using OECS;
|
|
||||||
using R3;
|
|
||||||
|
|
||||||
namespace TicTacToe;
|
|
||||||
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
private static readonly string SavePath = Path.Combine(
|
|
||||||
AppContext.BaseDirectory, "save.bin");
|
|
||||||
|
|
||||||
public static void Main()
|
|
||||||
{
|
|
||||||
var world = new World();
|
|
||||||
var reactivityLog = new List<string>();
|
|
||||||
SetupReactivityLogging(world, reactivityLog);
|
|
||||||
|
|
||||||
// ── Try to load a saved game ──────────────────────────────────
|
|
||||||
|
|
||||||
bool loaded = false;
|
|
||||||
if (File.Exists(SavePath))
|
|
||||||
{
|
|
||||||
Console.Write("Saved game found. Load it? (y/n): ");
|
|
||||||
if (Console.ReadLine()?.Trim().ToLower() == "y")
|
|
||||||
{
|
|
||||||
using var stream = File.OpenRead(SavePath);
|
|
||||||
WorldSerializer.Load(world, stream);
|
|
||||||
loaded = true;
|
|
||||||
Console.WriteLine("Game loaded!");
|
|
||||||
Console.WriteLine();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Load fresh board from CSV (if not loaded) ─────────────────
|
|
||||||
|
|
||||||
if (!loaded)
|
|
||||||
{
|
|
||||||
var csvPath = Path.Combine(AppContext.BaseDirectory, "Data", "board.csv");
|
|
||||||
CsvLoader.LoadCells(world, csvPath);
|
|
||||||
world.PostChanges();
|
|
||||||
|
|
||||||
world.SetSingleton(new GameState
|
|
||||||
{
|
|
||||||
CurrentPlayer = Player.X,
|
|
||||||
Status = GameStatus.Playing,
|
|
||||||
MoveCount = 0
|
|
||||||
});
|
|
||||||
world.PostChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Wire up systems ───────────────────────────────────────────
|
|
||||||
|
|
||||||
var group = new SystemGroup(world);
|
|
||||||
group.Add(new WinCheckSystem());
|
|
||||||
group.Add(new RenderSystem());
|
|
||||||
|
|
||||||
// ── Game loop ─────────────────────────────────────────────────
|
|
||||||
|
|
||||||
group.RunLogical();
|
|
||||||
PrintReactivityLog(reactivityLog);
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ref var state = ref world.GetSingleton<GameState>();
|
|
||||||
if (state.Status != GameStatus.Playing)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var input = Console.ReadLine();
|
|
||||||
if (string.IsNullOrWhiteSpace(input))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// "save" command.
|
|
||||||
if (input.Trim().ToLower() == "save")
|
|
||||||
{
|
|
||||||
SaveGame(world);
|
|
||||||
Console.WriteLine(" Game saved.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parts = input.Trim().Split(' ', StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (parts.Length != 2 ||
|
|
||||||
!int.TryParse(parts[0], out var row) ||
|
|
||||||
!int.TryParse(parts[1], out var col))
|
|
||||||
{
|
|
||||||
Console.WriteLine(" Invalid input. Try \"1 2\" or \"save\".");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row < 0 || row > 2 || col < 0 || col > 2)
|
|
||||||
{
|
|
||||||
Console.WriteLine(" Row and col must be 0–2.");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
world.Commands.Enqueue(new PlaceMarkCommand { Row = row, Col = col });
|
|
||||||
group.RunLogical();
|
|
||||||
PrintReactivityLog(reactivityLog);
|
|
||||||
|
|
||||||
// Auto-save after every move.
|
|
||||||
SaveGame(world);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Final state ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine("=== Game over ===");
|
|
||||||
PrintReactivityLog(reactivityLog);
|
|
||||||
|
|
||||||
// Clean up save file on game over.
|
|
||||||
if (File.Exists(SavePath))
|
|
||||||
File.Delete(SavePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SaveGame(World world)
|
|
||||||
{
|
|
||||||
using var stream = File.Create(SavePath);
|
|
||||||
WorldSerializer.Save(world, stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void SetupReactivityLogging(World world, List<string> log)
|
|
||||||
{
|
|
||||||
world.ObserveEntityChanges().Subscribe(change =>
|
|
||||||
{
|
|
||||||
if (change.ComponentType != null) return;
|
|
||||||
log.Add($"[react] {change}");
|
|
||||||
});
|
|
||||||
|
|
||||||
world.ObserveComponentChanges<Mark>().Subscribe(change =>
|
|
||||||
{
|
|
||||||
log.Add($"[react] {change}");
|
|
||||||
});
|
|
||||||
|
|
||||||
world.ObserveComponentChanges<GameState>().Subscribe(change =>
|
|
||||||
{
|
|
||||||
log.Add($"[react] {change}");
|
|
||||||
});
|
|
||||||
|
|
||||||
var markedQuery = world.Query().With<Cell>().With<Mark>().Build();
|
|
||||||
world.ObserveQuery(markedQuery).Subscribe(change =>
|
|
||||||
{
|
|
||||||
log.Add($"[react:query] {change}");
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void PrintReactivityLog(List<string> log)
|
|
||||||
{
|
|
||||||
if (log.Count == 0) return;
|
|
||||||
Console.WriteLine("── reactivity log ──");
|
|
||||||
foreach (var entry in log)
|
|
||||||
Console.WriteLine(entry);
|
|
||||||
Console.WriteLine();
|
|
||||||
log.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
using OECS;
|
|
||||||
|
|
||||||
namespace TicTacToe;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Prints the board to the console.
|
|
||||||
/// </summary>
|
|
||||||
public class RenderSystem : ISystem
|
|
||||||
{
|
|
||||||
public void Run(World world)
|
|
||||||
{
|
|
||||||
var state = world.ReadSingleton<GameState>();
|
|
||||||
|
|
||||||
// Build a 3×3 grid.
|
|
||||||
var grid = new char[3, 3];
|
|
||||||
for (int r = 0; r < 3; r++)
|
|
||||||
for (int c = 0; c < 3; c++)
|
|
||||||
grid[r, c] = '.';
|
|
||||||
|
|
||||||
// Fill in placed marks using the iterator API.
|
|
||||||
using var markIter = world.Select<Cell, Mark>();
|
|
||||||
while (markIter.MoveNext())
|
|
||||||
{
|
|
||||||
grid[markIter.Current1.Row, markIter.Current1.Col] =
|
|
||||||
markIter.Current2.Player == Player.X ? 'X' : 'O';
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine(" Tic-Tac-Toe");
|
|
||||||
Console.WriteLine(" ═══════════");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.WriteLine(" 0 1 2");
|
|
||||||
Console.WriteLine(" ┌───┬───┬───┐");
|
|
||||||
for (int r = 0; r < 3; r++)
|
|
||||||
{
|
|
||||||
Console.Write($"{r} │");
|
|
||||||
for (int c = 0; c < 3; c++)
|
|
||||||
{
|
|
||||||
Console.Write($" {grid[r, c]} ");
|
|
||||||
if (c < 2) Console.Write("│");
|
|
||||||
}
|
|
||||||
Console.WriteLine("│");
|
|
||||||
if (r < 2) Console.WriteLine(" ├───┼───┼───┤");
|
|
||||||
}
|
|
||||||
Console.WriteLine(" └───┴───┴───┘");
|
|
||||||
Console.WriteLine();
|
|
||||||
|
|
||||||
// Status line.
|
|
||||||
var statusText = state.Status switch
|
|
||||||
{
|
|
||||||
GameStatus.Playing => $"{(state.CurrentPlayer == Player.X ? 'X' : 'O')}'s turn (move {state.MoveCount + 1})",
|
|
||||||
GameStatus.XWon => "X wins!",
|
|
||||||
GameStatus.OWon => "O wins!",
|
|
||||||
GameStatus.Draw => "It's a draw!",
|
|
||||||
_ => ""
|
|
||||||
};
|
|
||||||
Console.WriteLine($" {statusText}");
|
|
||||||
Console.WriteLine();
|
|
||||||
Console.Write(" Enter row col (e.g. \"1 2\"): ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
0 0
|
|
||||||
1 1
|
|
||||||
0 1
|
|
||||||
1 0
|
|
||||||
0 2
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
y
|
|
||||||
1 0
|
|
||||||
0 2
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
0 0
|
|
||||||
1 1
|
|
||||||
0 1
|
|
||||||
Loading…
Reference in New Issue