Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Dice game #2

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added identifier.sqlite
Empty file.
52 changes: 52 additions & 0 deletions src/Abstractions/CharBoard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,56 @@ public CharBoard Set(int r, int c, char value)
return new CharBoard(Size, newCells);
}
}

public record DiceBoard
{
private static readonly ConcurrentDictionary<int, DiceBoard> EmptyCache = new();
public static DiceBoard Empty(int size) => EmptyCache.GetOrAdd(size, size1 => new DiceBoard(size1));

public int Size { get; }
public string[] Cells { get; }

public string this[int r, int c] {
get {
var cellIndex = GetCellIndex(r, c);
if (cellIndex < 0 || cellIndex >= Cells.Length)
return " ";
return Cells[cellIndex];
}
}

public DiceBoard(int size)
{
if (size < 1)
throw new ArgumentOutOfRangeException(nameof(size));
Size = size;
Cells = new string[size * size];
for (int i = 0; i < size * size; i++) { Cells[i] = " "; }
}

[JsonConstructor]
public DiceBoard(int size, string[] cells)
{
if (size < 1)
throw new ArgumentOutOfRangeException(nameof(size));
if (size * size != cells.Length)
throw new ArgumentOutOfRangeException(nameof(size));
Size = size;
Cells = cells;
}

public int GetCellIndex(int r, int c) => r * Size + c;

public DiceBoard Set(int r, int c, int playerIndex, char value)
{
if (r < 0 || r >= Size)
throw new ArgumentOutOfRangeException(nameof(r));
if (c < 0 || c >= Size)
throw new ArgumentOutOfRangeException(nameof(c));
var cellIndex = GetCellIndex(r, c);
var cell = Cells[cellIndex];
cell = cell.Substring(0, playerIndex) + value + cell.Substring(playerIndex + 1);
return new DiceBoard(Size, Cells);
}
}
}
111 changes: 111 additions & 0 deletions src/Abstractions/Games/Dice.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
using System;
using Stl.DependencyInjection;

namespace BoardGames.Abstractions.Games
{
public record DiceState(DiceBoard Board,
int[] PlayerPositions,
int[] PlayerSteps,
int MoveIndex = 0,
int FirstPlayerIndex = 0,
int PlayersCount = 0
)

{
public int PlayerIndex => (MoveIndex + FirstPlayerIndex) % PlayersCount;
public DiceState() : this((DiceBoard) null!, (int[]) null!, (int[]) null!) { }
}

public record DiceMove(int PlayerIndex, int Value) : GameMove
{
public DiceMove() : this(0, 0) {}
}

[Service, ServiceAlias(typeof(IGameEngine), IsEnumerable = true)]
public class DiceEngine : GameEngine<DiceState, DiceMove>
{
public static int BoardSize { get; } = 8;
public override string Id => "dice";
public override string Title => "Dice";
public override string Icon => "fa-dice-five";
public override int MinPlayerCount => 2;
public override int MaxPlayerCount => 4;
public override bool AutoStart => true;

public override Game Start(Game game)
{
var positions = new int[] {-1, -1, -1, -1};
var steps = new int[] {0, 0, 0, 0};
var rnd = new Random();
var playersCount = game.Players.Count;
var firstPlayerIndex = rnd.Next(0, playersCount);
var state = new DiceState(DiceBoard.Empty(BoardSize), positions, steps, 0, firstPlayerIndex, playersCount);
var player = game.Players[state.PlayerIndex];
return game with {
StateJson = SerializeState(state),
StateMessage = StandardMessages.MoveTurn(new AppUser(player.UserId)),
};
}

public override Game Move(Game game, DiceMove move)
{
if (game.Stage == GameStage.Ended)
throw new ApplicationException("Game is ended.");
var state = DeserializeState(game.StateJson);
if (move.PlayerIndex != state.PlayerIndex)
throw new ApplicationException("It's another player's turn.");
var board = state.Board;
var player = game.Players[state.PlayerIndex];

state.PlayerPositions[state.PlayerIndex] += move.Value;
var playerPosition = state.PlayerPositions[state.PlayerIndex];

if (playerPosition == 10 || playerPosition == 27 || playerPosition == 44) {
state.PlayerPositions[state.PlayerIndex] += 3;
playerPosition += 3;
}
if (playerPosition == 20 || playerPosition == 35 || playerPosition == 54) {
state.PlayerPositions[state.PlayerIndex] -= 3;
playerPosition -= 3;
}

if (playerPosition >= (BoardSize * BoardSize) - 1)
playerPosition = (BoardSize * BoardSize) - 1;

state.PlayerSteps[state.PlayerIndex] += 1;
var playerSteps = state.PlayerSteps[state.PlayerIndex];
var scores = state.PlayerPositions;

if (playerPosition >= (BoardSize * BoardSize) - 1) {
var newState = state with {Board = board, MoveIndex = state.MoveIndex + 1, PlayerPositions = scores};
var newGame = game with {StateJson = SerializeState(newState)};
newGame = IncrementPlayerScore(newGame, move.PlayerIndex, 1) with {
StateMessage = StandardMessages.WinWithScore(new AppUser(player.UserId), game, playerSteps),
Stage = GameStage.Ended,
};
return newGame;
}

var rowAndCol = GetRowAndColValues(playerPosition);
var nextBoard = board.Set(rowAndCol.Item1, rowAndCol.Item2, state.PlayerIndex, 'X');
var nextState = state with {
Board = nextBoard,
MoveIndex = state.MoveIndex + 1,
PlayerPositions = scores,
};
var nextPlayer = game.Players[nextState.PlayerIndex];
var nextGame = game with {StateJson = SerializeState(nextState)};
nextGame = nextGame with {
StateMessage = StandardMessages.MoveTurn(new AppUser(nextPlayer.UserId)),
};
return nextGame;
}

private (int, int) GetRowAndColValues(long value)
{
var row = value / BoardSize;
var col = value % BoardSize;
return ((int)row, (int)col);
}
}
}
2 changes: 0 additions & 2 deletions src/Abstractions/Games/Gomoku.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ public enum GomokuGameEndingKind
public record GomokuState(CharBoard Board, int MoveIndex = 0, int FirstPlayerIndex = 0)
{
public int PlayerIndex => (MoveIndex + FirstPlayerIndex) % 2;
public int NextPlayerIndex => (PlayerIndex + 1) % 2;

public GomokuState() : this((CharBoard) null!) { }
}

Expand Down
1 change: 1 addition & 0 deletions src/Host/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public void ConfigureServices(IServiceCollection services)
HostSettings = tmpServices.GetRequiredService<HostSettings>();

// DbContext & related services

var appTempDir = PathEx.GetApplicationTempDirectory("", true);
var sqliteDbPath = appTempDir & "App_v0_1.db";
services.AddDbContextFactory<AppDbContext>(builder => {
Expand Down
5 changes: 5 additions & 0 deletions src/SharedServices/ClientServicesModule.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using BoardGames.Abstractions;
using BoardGames.Abstractions.Games;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Pluralize.NET;
using Stl.Extensibility;
using Stl.Fusion;
Expand All @@ -16,6 +20,7 @@ public ClientServicesModule(IServiceCollection services, IServiceProvider module

public override void Use()
{

// Other UI-related services
Services.AddSingleton<IPluralize, Pluralizer>();
Services.AddFusion().AddLiveClock();
Expand Down
Loading